blob: 84063d4ad306d55b01caba87ce82bbf71129a55e [file] [log] [blame]
perkj26091b12016-09-01 01:17:40 -07001/*
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
sprangfe627f32017-03-29 08:24:59 -070011#include <algorithm>
perkj803d97f2016-11-01 11:45:46 -070012#include <limits>
Per512ecb32016-09-23 15:52:06 +020013#include <utility>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "api/video/i420_buffer.h"
16#include "media/base/videoadapter.h"
17#include "modules/video_coding/codecs/vp8/temporal_layers.h"
Sergey Silkin86684962018-03-28 19:32:37 +020018#include "modules/video_coding/codecs/vp9/include/vp9_globals.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "modules/video_coding/utility/default_video_bitrate_allocator.h"
20#include "rtc_base/fakeclock.h"
21#include "rtc_base/logging.h"
Stefan Holmerdbdb3a02018-07-17 16:03:46 +020022#include "rtc_base/refcountedobject.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "system_wrappers/include/metrics_default.h"
24#include "system_wrappers/include/sleep.h"
Niels Möller4db138e2018-04-19 09:04:13 +020025#include "test/encoder_proxy_factory.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "test/encoder_settings.h"
27#include "test/fake_encoder.h"
Kári Tristan Helgason639602a2018-08-02 10:51:40 +020028#include "test/field_trial.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020029#include "test/frame_generator.h"
30#include "test/gmock.h"
31#include "test/gtest.h"
32#include "video/send_statistics_proxy.h"
33#include "video/video_stream_encoder.h"
perkj26091b12016-09-01 01:17:40 -070034
Mirko Bonadei948b7e32018-08-14 07:23:21 +000035namespace {
36const int kMinPixelsPerFrame = 320 * 180;
37const int kMinFramerateFps = 2;
38const int kMinBalancedFramerateFps = 7;
39const int64_t kFrameTimeoutMs = 100;
40} // namespace
41
perkj26091b12016-09-01 01:17:40 -070042namespace webrtc {
43
sprangb1ca0732017-02-01 08:38:12 -080044using ScaleReason = AdaptationObserverInterface::AdaptReason;
sprang57c2fff2017-01-16 06:24:02 -080045using ::testing::_;
46using ::testing::Return;
kthelgason876222f2016-11-29 01:44:11 -080047
perkj803d97f2016-11-01 11:45:46 -070048namespace {
asapersson5f7226f2016-11-25 04:37:00 -080049const size_t kMaxPayloadLength = 1440;
kthelgason2bc68642017-02-07 07:02:22 -080050const int kTargetBitrateBps = 1000000;
51const int kLowTargetBitrateBps = kTargetBitrateBps / 10;
52const int kMaxInitialFramedrop = 4;
sprangfda496a2017-06-15 04:21:07 -070053const int kDefaultFramerate = 30;
asapersson5f7226f2016-11-25 04:37:00 -080054
perkj803d97f2016-11-01 11:45:46 -070055class TestBuffer : public webrtc::I420Buffer {
56 public:
57 TestBuffer(rtc::Event* event, int width, int height)
58 : I420Buffer(width, height), event_(event) {}
59
60 private:
61 friend class rtc::RefCountedObject<TestBuffer>;
62 ~TestBuffer() override {
63 if (event_)
64 event_->Set();
65 }
66 rtc::Event* const event_;
67};
68
Niels Möller7dc26b72017-12-06 10:27:48 +010069class CpuOveruseDetectorProxy : public OveruseFrameDetector {
70 public:
Niels Möllerd1f7eb62018-03-28 16:40:58 +020071 explicit CpuOveruseDetectorProxy(CpuOveruseMetricsObserver* metrics_observer)
72 : OveruseFrameDetector(metrics_observer),
Niels Möller7dc26b72017-12-06 10:27:48 +010073 last_target_framerate_fps_(-1) {}
74 virtual ~CpuOveruseDetectorProxy() {}
75
76 void OnTargetFramerateUpdated(int framerate_fps) override {
77 rtc::CritScope cs(&lock_);
78 last_target_framerate_fps_ = framerate_fps;
79 OveruseFrameDetector::OnTargetFramerateUpdated(framerate_fps);
80 }
81
82 int GetLastTargetFramerate() {
83 rtc::CritScope cs(&lock_);
84 return last_target_framerate_fps_;
85 }
86
Niels Möller4db138e2018-04-19 09:04:13 +020087 CpuOveruseOptions GetOptions() { return options_; }
88
Niels Möller7dc26b72017-12-06 10:27:48 +010089 private:
90 rtc::CriticalSection lock_;
91 int last_target_framerate_fps_ RTC_GUARDED_BY(lock_);
92};
93
mflodmancc3d4422017-08-03 08:27:51 -070094class VideoStreamEncoderUnderTest : public VideoStreamEncoder {
perkj803d97f2016-11-01 11:45:46 -070095 public:
Niels Möller213618e2018-07-24 09:29:58 +020096 VideoStreamEncoderUnderTest(SendStatisticsProxy* stats_proxy,
97 const VideoStreamEncoderSettings& settings)
Yves Gerey665174f2018-06-19 15:03:05 +020098 : VideoStreamEncoder(1 /* number_of_cores */,
99 stats_proxy,
100 settings,
101 nullptr /* pre_encode_callback */,
102 std::unique_ptr<OveruseFrameDetector>(
103 overuse_detector_proxy_ =
104 new CpuOveruseDetectorProxy(stats_proxy))) {}
perkj803d97f2016-11-01 11:45:46 -0700105
sprangb1ca0732017-02-01 08:38:12 -0800106 void PostTaskAndWait(bool down, AdaptReason reason) {
perkj803d97f2016-11-01 11:45:46 -0700107 rtc::Event event(false, false);
kthelgason876222f2016-11-29 01:44:11 -0800108 encoder_queue()->PostTask([this, &event, reason, down] {
sprangb1ca0732017-02-01 08:38:12 -0800109 down ? AdaptDown(reason) : AdaptUp(reason);
perkj803d97f2016-11-01 11:45:46 -0700110 event.Set();
111 });
perkj070ba852017-02-16 15:46:27 -0800112 ASSERT_TRUE(event.Wait(5000));
perkj803d97f2016-11-01 11:45:46 -0700113 }
114
kthelgason2fc52542017-03-03 00:24:41 -0800115 // This is used as a synchronisation mechanism, to make sure that the
116 // encoder queue is not blocked before we start sending it frames.
117 void WaitUntilTaskQueueIsIdle() {
118 rtc::Event event(false, false);
Yves Gerey665174f2018-06-19 15:03:05 +0200119 encoder_queue()->PostTask([&event] { event.Set(); });
kthelgason2fc52542017-03-03 00:24:41 -0800120 ASSERT_TRUE(event.Wait(5000));
121 }
122
sprangb1ca0732017-02-01 08:38:12 -0800123 void TriggerCpuOveruse() { PostTaskAndWait(true, AdaptReason::kCpu); }
kthelgason876222f2016-11-29 01:44:11 -0800124
sprangb1ca0732017-02-01 08:38:12 -0800125 void TriggerCpuNormalUsage() { PostTaskAndWait(false, AdaptReason::kCpu); }
kthelgason876222f2016-11-29 01:44:11 -0800126
sprangb1ca0732017-02-01 08:38:12 -0800127 void TriggerQualityLow() { PostTaskAndWait(true, AdaptReason::kQuality); }
kthelgason876222f2016-11-29 01:44:11 -0800128
sprangb1ca0732017-02-01 08:38:12 -0800129 void TriggerQualityHigh() { PostTaskAndWait(false, AdaptReason::kQuality); }
sprangfda496a2017-06-15 04:21:07 -0700130
Niels Möller7dc26b72017-12-06 10:27:48 +0100131 CpuOveruseDetectorProxy* overuse_detector_proxy_;
perkj803d97f2016-11-01 11:45:46 -0700132};
133
asapersson5f7226f2016-11-25 04:37:00 -0800134class VideoStreamFactory
135 : public VideoEncoderConfig::VideoStreamFactoryInterface {
136 public:
sprangfda496a2017-06-15 04:21:07 -0700137 explicit VideoStreamFactory(size_t num_temporal_layers, int framerate)
138 : num_temporal_layers_(num_temporal_layers), framerate_(framerate) {
asapersson5f7226f2016-11-25 04:37:00 -0800139 EXPECT_GT(num_temporal_layers, 0u);
sprangfda496a2017-06-15 04:21:07 -0700140 EXPECT_GT(framerate, 0);
asapersson5f7226f2016-11-25 04:37:00 -0800141 }
142
143 private:
144 std::vector<VideoStream> CreateEncoderStreams(
145 int width,
146 int height,
147 const VideoEncoderConfig& encoder_config) override {
148 std::vector<VideoStream> streams =
149 test::CreateVideoStreams(width, height, encoder_config);
150 for (VideoStream& stream : streams) {
Sergey Silkina796a7e2018-03-01 15:11:29 +0100151 stream.num_temporal_layers = num_temporal_layers_;
sprangfda496a2017-06-15 04:21:07 -0700152 stream.max_framerate = framerate_;
asapersson5f7226f2016-11-25 04:37:00 -0800153 }
154 return streams;
155 }
sprangfda496a2017-06-15 04:21:07 -0700156
asapersson5f7226f2016-11-25 04:37:00 -0800157 const size_t num_temporal_layers_;
sprangfda496a2017-06-15 04:21:07 -0700158 const int framerate_;
asapersson5f7226f2016-11-25 04:37:00 -0800159};
160
sprangb1ca0732017-02-01 08:38:12 -0800161class AdaptingFrameForwarder : public test::FrameForwarder {
162 public:
163 AdaptingFrameForwarder() : adaptation_enabled_(false) {}
asaperssonfab67072017-04-04 05:51:49 -0700164 ~AdaptingFrameForwarder() override {}
sprangb1ca0732017-02-01 08:38:12 -0800165
166 void set_adaptation_enabled(bool enabled) {
167 rtc::CritScope cs(&crit_);
168 adaptation_enabled_ = enabled;
169 }
170
asaperssonfab67072017-04-04 05:51:49 -0700171 bool adaption_enabled() const {
sprangb1ca0732017-02-01 08:38:12 -0800172 rtc::CritScope cs(&crit_);
173 return adaptation_enabled_;
174 }
175
asapersson09f05612017-05-15 23:40:18 -0700176 rtc::VideoSinkWants last_wants() const {
177 rtc::CritScope cs(&crit_);
178 return last_wants_;
179 }
180
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200181 absl::optional<int> last_sent_width() const { return last_width_; }
182 absl::optional<int> last_sent_height() const { return last_height_; }
Jonathan Yubc771b72017-12-08 17:04:29 -0800183
sprangb1ca0732017-02-01 08:38:12 -0800184 void IncomingCapturedFrame(const VideoFrame& video_frame) override {
185 int cropped_width = 0;
186 int cropped_height = 0;
187 int out_width = 0;
188 int out_height = 0;
sprangc5d62e22017-04-02 23:53:04 -0700189 if (adaption_enabled()) {
190 if (adapter_.AdaptFrameResolution(
191 video_frame.width(), video_frame.height(),
192 video_frame.timestamp_us() * 1000, &cropped_width,
193 &cropped_height, &out_width, &out_height)) {
194 VideoFrame adapted_frame(new rtc::RefCountedObject<TestBuffer>(
195 nullptr, out_width, out_height),
196 99, 99, kVideoRotation_0);
197 adapted_frame.set_ntp_time_ms(video_frame.ntp_time_ms());
198 test::FrameForwarder::IncomingCapturedFrame(adapted_frame);
Jonathan Yubc771b72017-12-08 17:04:29 -0800199 last_width_.emplace(adapted_frame.width());
200 last_height_.emplace(adapted_frame.height());
201 } else {
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200202 last_width_ = absl::nullopt;
203 last_height_ = absl::nullopt;
sprangc5d62e22017-04-02 23:53:04 -0700204 }
sprangb1ca0732017-02-01 08:38:12 -0800205 } else {
206 test::FrameForwarder::IncomingCapturedFrame(video_frame);
Jonathan Yubc771b72017-12-08 17:04:29 -0800207 last_width_.emplace(video_frame.width());
208 last_height_.emplace(video_frame.height());
sprangb1ca0732017-02-01 08:38:12 -0800209 }
210 }
211
212 void AddOrUpdateSink(rtc::VideoSinkInterface<VideoFrame>* sink,
213 const rtc::VideoSinkWants& wants) override {
214 rtc::CritScope cs(&crit_);
asapersson09f05612017-05-15 23:40:18 -0700215 last_wants_ = sink_wants();
sprangc5d62e22017-04-02 23:53:04 -0700216 adapter_.OnResolutionFramerateRequest(wants.target_pixel_count,
217 wants.max_pixel_count,
218 wants.max_framerate_fps);
sprangb1ca0732017-02-01 08:38:12 -0800219 test::FrameForwarder::AddOrUpdateSink(sink, wants);
220 }
sprangb1ca0732017-02-01 08:38:12 -0800221 cricket::VideoAdapter adapter_;
danilchapa37de392017-09-09 04:17:22 -0700222 bool adaptation_enabled_ RTC_GUARDED_BY(crit_);
223 rtc::VideoSinkWants last_wants_ RTC_GUARDED_BY(crit_);
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200224 absl::optional<int> last_width_;
225 absl::optional<int> last_height_;
sprangb1ca0732017-02-01 08:38:12 -0800226};
sprangc5d62e22017-04-02 23:53:04 -0700227
Niels Möller213618e2018-07-24 09:29:58 +0200228// TODO(nisse): Mock only VideoStreamEncoderObserver.
sprangc5d62e22017-04-02 23:53:04 -0700229class MockableSendStatisticsProxy : public SendStatisticsProxy {
230 public:
231 MockableSendStatisticsProxy(Clock* clock,
232 const VideoSendStream::Config& config,
233 VideoEncoderConfig::ContentType content_type)
234 : SendStatisticsProxy(clock, config, content_type) {}
235
236 VideoSendStream::Stats GetStats() override {
237 rtc::CritScope cs(&lock_);
238 if (mock_stats_)
239 return *mock_stats_;
240 return SendStatisticsProxy::GetStats();
241 }
242
Niels Möller213618e2018-07-24 09:29:58 +0200243 int GetInputFrameRate() const override {
244 rtc::CritScope cs(&lock_);
245 if (mock_stats_)
246 return mock_stats_->input_frame_rate;
247 return SendStatisticsProxy::GetInputFrameRate();
248 }
sprangc5d62e22017-04-02 23:53:04 -0700249 void SetMockStats(const VideoSendStream::Stats& stats) {
250 rtc::CritScope cs(&lock_);
251 mock_stats_.emplace(stats);
252 }
253
254 void ResetMockStats() {
255 rtc::CritScope cs(&lock_);
256 mock_stats_.reset();
257 }
258
259 private:
260 rtc::CriticalSection lock_;
Danil Chapovalovb9b146c2018-06-15 12:28:07 +0200261 absl::optional<VideoSendStream::Stats> mock_stats_ RTC_GUARDED_BY(lock_);
sprangc5d62e22017-04-02 23:53:04 -0700262};
263
sprang4847ae62017-06-27 07:06:52 -0700264class MockBitrateObserver : public VideoBitrateAllocationObserver {
265 public:
Erik Språng566124a2018-04-23 12:32:22 +0200266 MOCK_METHOD1(OnBitrateAllocationUpdated, void(const VideoBitrateAllocation&));
sprang4847ae62017-06-27 07:06:52 -0700267};
268
perkj803d97f2016-11-01 11:45:46 -0700269} // namespace
270
mflodmancc3d4422017-08-03 08:27:51 -0700271class VideoStreamEncoderTest : public ::testing::Test {
perkj26091b12016-09-01 01:17:40 -0700272 public:
273 static const int kDefaultTimeoutMs = 30 * 1000;
274
mflodmancc3d4422017-08-03 08:27:51 -0700275 VideoStreamEncoderTest()
perkj26091b12016-09-01 01:17:40 -0700276 : video_send_config_(VideoSendStream::Config(nullptr)),
perkjfa10b552016-10-02 23:45:26 -0700277 codec_width_(320),
278 codec_height_(240),
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000279 max_framerate_(30),
perkj26091b12016-09-01 01:17:40 -0700280 fake_encoder_(),
Niels Möller4db138e2018-04-19 09:04:13 +0200281 encoder_factory_(&fake_encoder_),
sprangc5d62e22017-04-02 23:53:04 -0700282 stats_proxy_(new MockableSendStatisticsProxy(
perkj803d97f2016-11-01 11:45:46 -0700283 Clock::GetRealTimeClock(),
284 video_send_config_,
285 webrtc::VideoEncoderConfig::ContentType::kRealtimeVideo)),
perkj26091b12016-09-01 01:17:40 -0700286 sink_(&fake_encoder_) {}
287
288 void SetUp() override {
perkj803d97f2016-11-01 11:45:46 -0700289 metrics::Reset();
perkj26091b12016-09-01 01:17:40 -0700290 video_send_config_ = VideoSendStream::Config(nullptr);
Niels Möller4db138e2018-04-19 09:04:13 +0200291 video_send_config_.encoder_settings.encoder_factory = &encoder_factory_;
Niels Möller259a4972018-04-05 15:36:51 +0200292 video_send_config_.rtp.payload_name = "FAKE";
293 video_send_config_.rtp.payload_type = 125;
perkj26091b12016-09-01 01:17:40 -0700294
Per512ecb32016-09-23 15:52:06 +0200295 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +0200296 test::FillEncoderConfiguration(kVideoCodecVP8, 1, &video_encoder_config);
sprang4847ae62017-06-27 07:06:52 -0700297 video_encoder_config.video_stream_factory =
298 new rtc::RefCountedObject<VideoStreamFactory>(1, max_framerate_);
Erik Språng08127a92016-11-16 16:41:30 +0100299 video_encoder_config_ = video_encoder_config.Copy();
sprang4847ae62017-06-27 07:06:52 -0700300
301 // Framerate limit is specified by the VideoStreamFactory.
302 std::vector<VideoStream> streams =
303 video_encoder_config.video_stream_factory->CreateEncoderStreams(
304 codec_width_, codec_height_, video_encoder_config);
305 max_framerate_ = streams[0].max_framerate;
306 fake_clock_.SetTimeMicros(1234);
307
Niels Möllerf1338562018-04-26 09:51:47 +0200308 ConfigureEncoder(std::move(video_encoder_config));
asapersson5f7226f2016-11-25 04:37:00 -0800309 }
310
Niels Möllerf1338562018-04-26 09:51:47 +0200311 void ConfigureEncoder(VideoEncoderConfig video_encoder_config) {
mflodmancc3d4422017-08-03 08:27:51 -0700312 if (video_stream_encoder_)
313 video_stream_encoder_->Stop();
314 video_stream_encoder_.reset(new VideoStreamEncoderUnderTest(
perkj803d97f2016-11-01 11:45:46 -0700315 stats_proxy_.get(), video_send_config_.encoder_settings));
mflodmancc3d4422017-08-03 08:27:51 -0700316 video_stream_encoder_->SetSink(&sink_, false /* rotation_applied */);
317 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700318 &video_source_, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
mflodmancc3d4422017-08-03 08:27:51 -0700319 video_stream_encoder_->SetStartBitrate(kTargetBitrateBps);
320 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +0200321 kMaxPayloadLength);
mflodmancc3d4422017-08-03 08:27:51 -0700322 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
asapersson5f7226f2016-11-25 04:37:00 -0800323 }
324
325 void ResetEncoder(const std::string& payload_name,
326 size_t num_streams,
327 size_t num_temporal_layers,
emircanbbcc3562017-08-18 00:28:40 -0700328 unsigned char num_spatial_layers,
sprang4847ae62017-06-27 07:06:52 -0700329 bool screenshare) {
Niels Möller259a4972018-04-05 15:36:51 +0200330 video_send_config_.rtp.payload_name = payload_name;
asapersson5f7226f2016-11-25 04:37:00 -0800331
332 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +0200333 video_encoder_config.codec_type = PayloadStringToCodecType(payload_name);
asapersson5f7226f2016-11-25 04:37:00 -0800334 video_encoder_config.number_of_streams = num_streams;
kthelgason2bc68642017-02-07 07:02:22 -0800335 video_encoder_config.max_bitrate_bps = kTargetBitrateBps;
asapersson5f7226f2016-11-25 04:37:00 -0800336 video_encoder_config.video_stream_factory =
sprangfda496a2017-06-15 04:21:07 -0700337 new rtc::RefCountedObject<VideoStreamFactory>(num_temporal_layers,
338 kDefaultFramerate);
sprang4847ae62017-06-27 07:06:52 -0700339 video_encoder_config.content_type =
340 screenshare ? VideoEncoderConfig::ContentType::kScreen
341 : VideoEncoderConfig::ContentType::kRealtimeVideo;
emircanbbcc3562017-08-18 00:28:40 -0700342 if (payload_name == "VP9") {
343 VideoCodecVP9 vp9_settings = VideoEncoder::GetDefaultVp9Settings();
344 vp9_settings.numberOfSpatialLayers = num_spatial_layers;
345 video_encoder_config.encoder_specific_settings =
346 new rtc::RefCountedObject<
347 VideoEncoderConfig::Vp9EncoderSpecificSettings>(vp9_settings);
348 }
Niels Möllerf1338562018-04-26 09:51:47 +0200349 ConfigureEncoder(std::move(video_encoder_config));
perkj26091b12016-09-01 01:17:40 -0700350 }
351
sprang57c2fff2017-01-16 06:24:02 -0800352 VideoFrame CreateFrame(int64_t ntp_time_ms,
353 rtc::Event* destruction_event) const {
Per512ecb32016-09-23 15:52:06 +0200354 VideoFrame frame(new rtc::RefCountedObject<TestBuffer>(
355 destruction_event, codec_width_, codec_height_),
356 99, 99, kVideoRotation_0);
sprang57c2fff2017-01-16 06:24:02 -0800357 frame.set_ntp_time_ms(ntp_time_ms);
perkj26091b12016-09-01 01:17:40 -0700358 return frame;
359 }
360
sprang57c2fff2017-01-16 06:24:02 -0800361 VideoFrame CreateFrame(int64_t ntp_time_ms, int width, int height) const {
perkj803d97f2016-11-01 11:45:46 -0700362 VideoFrame frame(
363 new rtc::RefCountedObject<TestBuffer>(nullptr, width, height), 99, 99,
364 kVideoRotation_0);
sprang57c2fff2017-01-16 06:24:02 -0800365 frame.set_ntp_time_ms(ntp_time_ms);
sprangc5d62e22017-04-02 23:53:04 -0700366 frame.set_timestamp_us(ntp_time_ms * 1000);
perkj803d97f2016-11-01 11:45:46 -0700367 return frame;
368 }
369
asapersson02465b82017-04-10 01:12:52 -0700370 void VerifyNoLimitation(const rtc::VideoSinkWants& wants) {
asapersson02465b82017-04-10 01:12:52 -0700371 EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_framerate_fps);
asapersson09f05612017-05-15 23:40:18 -0700372 EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_pixel_count);
373 EXPECT_FALSE(wants.target_pixel_count);
asapersson02465b82017-04-10 01:12:52 -0700374 }
375
asapersson09f05612017-05-15 23:40:18 -0700376 void VerifyFpsEqResolutionEq(const rtc::VideoSinkWants& wants1,
377 const rtc::VideoSinkWants& wants2) {
378 EXPECT_EQ(wants1.max_framerate_fps, wants2.max_framerate_fps);
379 EXPECT_EQ(wants1.max_pixel_count, wants2.max_pixel_count);
380 }
381
382 void VerifyFpsMaxResolutionLt(const rtc::VideoSinkWants& wants1,
383 const rtc::VideoSinkWants& wants2) {
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000384 EXPECT_EQ(std::numeric_limits<int>::max(), wants1.max_framerate_fps);
asapersson09f05612017-05-15 23:40:18 -0700385 EXPECT_LT(wants1.max_pixel_count, wants2.max_pixel_count);
386 EXPECT_GT(wants1.max_pixel_count, 0);
387 }
388
389 void VerifyFpsMaxResolutionGt(const rtc::VideoSinkWants& wants1,
390 const rtc::VideoSinkWants& wants2) {
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000391 EXPECT_EQ(std::numeric_limits<int>::max(), wants1.max_framerate_fps);
asapersson09f05612017-05-15 23:40:18 -0700392 EXPECT_GT(wants1.max_pixel_count, wants2.max_pixel_count);
393 }
394
asaperssonf7e294d2017-06-13 23:25:22 -0700395 void VerifyFpsMaxResolutionEq(const rtc::VideoSinkWants& wants1,
396 const rtc::VideoSinkWants& wants2) {
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000397 EXPECT_EQ(std::numeric_limits<int>::max(), wants1.max_framerate_fps);
asaperssonf7e294d2017-06-13 23:25:22 -0700398 EXPECT_EQ(wants1.max_pixel_count, wants2.max_pixel_count);
399 }
400
401 void VerifyFpsLtResolutionEq(const rtc::VideoSinkWants& wants1,
402 const rtc::VideoSinkWants& wants2) {
403 EXPECT_LT(wants1.max_framerate_fps, wants2.max_framerate_fps);
404 EXPECT_EQ(wants1.max_pixel_count, wants2.max_pixel_count);
405 }
406
407 void VerifyFpsGtResolutionEq(const rtc::VideoSinkWants& wants1,
408 const rtc::VideoSinkWants& wants2) {
409 EXPECT_GT(wants1.max_framerate_fps, wants2.max_framerate_fps);
410 EXPECT_EQ(wants1.max_pixel_count, wants2.max_pixel_count);
411 }
412
413 void VerifyFpsEqResolutionLt(const rtc::VideoSinkWants& wants1,
414 const rtc::VideoSinkWants& wants2) {
415 EXPECT_EQ(wants1.max_framerate_fps, wants2.max_framerate_fps);
416 EXPECT_LT(wants1.max_pixel_count, wants2.max_pixel_count);
417 EXPECT_GT(wants1.max_pixel_count, 0);
418 }
419
420 void VerifyFpsEqResolutionGt(const rtc::VideoSinkWants& wants1,
421 const rtc::VideoSinkWants& wants2) {
422 EXPECT_EQ(wants1.max_framerate_fps, wants2.max_framerate_fps);
423 EXPECT_GT(wants1.max_pixel_count, wants2.max_pixel_count);
424 }
425
asapersson09f05612017-05-15 23:40:18 -0700426 void VerifyFpsMaxResolutionLt(const rtc::VideoSinkWants& wants,
427 int pixel_count) {
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000428 EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_framerate_fps);
asapersson02465b82017-04-10 01:12:52 -0700429 EXPECT_LT(wants.max_pixel_count, pixel_count);
430 EXPECT_GT(wants.max_pixel_count, 0);
asapersson09f05612017-05-15 23:40:18 -0700431 }
432
433 void VerifyFpsLtResolutionMax(const rtc::VideoSinkWants& wants, int fps) {
434 EXPECT_LT(wants.max_framerate_fps, fps);
435 EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_pixel_count);
436 EXPECT_FALSE(wants.target_pixel_count);
asapersson02465b82017-04-10 01:12:52 -0700437 }
438
asaperssonf7e294d2017-06-13 23:25:22 -0700439 void VerifyFpsEqResolutionMax(const rtc::VideoSinkWants& wants,
440 int expected_fps) {
441 EXPECT_EQ(expected_fps, wants.max_framerate_fps);
442 EXPECT_EQ(std::numeric_limits<int>::max(), wants.max_pixel_count);
443 EXPECT_FALSE(wants.target_pixel_count);
444 }
445
Jonathan Yubc771b72017-12-08 17:04:29 -0800446 void VerifyBalancedModeFpsRange(const rtc::VideoSinkWants& wants,
447 int last_frame_pixels) {
448 // Balanced mode should always scale FPS to the desired range before
449 // attempting to scale resolution.
450 int fps_limit = wants.max_framerate_fps;
451 if (last_frame_pixels <= 320 * 240) {
452 EXPECT_TRUE(7 <= fps_limit && fps_limit <= 10);
453 } else if (last_frame_pixels <= 480 * 270) {
454 EXPECT_TRUE(10 <= fps_limit && fps_limit <= 15);
455 } else if (last_frame_pixels <= 640 * 480) {
456 EXPECT_LE(15, fps_limit);
457 } else {
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000458 EXPECT_EQ(std::numeric_limits<int>::max(), fps_limit);
Jonathan Yubc771b72017-12-08 17:04:29 -0800459 }
460 }
461
sprang4847ae62017-06-27 07:06:52 -0700462 void WaitForEncodedFrame(int64_t expected_ntp_time) {
463 sink_.WaitForEncodedFrame(expected_ntp_time);
464 fake_clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec / max_framerate_);
465 }
466
467 bool TimedWaitForEncodedFrame(int64_t expected_ntp_time, int64_t timeout_ms) {
468 bool ok = sink_.TimedWaitForEncodedFrame(expected_ntp_time, timeout_ms);
469 fake_clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec / max_framerate_);
470 return ok;
471 }
472
473 void WaitForEncodedFrame(uint32_t expected_width, uint32_t expected_height) {
474 sink_.WaitForEncodedFrame(expected_width, expected_height);
475 fake_clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec / max_framerate_);
476 }
477
478 void ExpectDroppedFrame() {
479 sink_.ExpectDroppedFrame();
480 fake_clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec / max_framerate_);
481 }
482
483 bool WaitForFrame(int64_t timeout_ms) {
484 bool ok = sink_.WaitForFrame(timeout_ms);
485 fake_clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerSec / max_framerate_);
486 return ok;
487 }
488
perkj26091b12016-09-01 01:17:40 -0700489 class TestEncoder : public test::FakeEncoder {
490 public:
491 TestEncoder()
492 : FakeEncoder(Clock::GetRealTimeClock()),
493 continue_encode_event_(false, false) {}
494
asaperssonfab67072017-04-04 05:51:49 -0700495 VideoCodec codec_config() const {
brandtre78d2662017-01-16 05:57:16 -0800496 rtc::CritScope lock(&crit_sect_);
perkjfa10b552016-10-02 23:45:26 -0700497 return config_;
498 }
499
500 void BlockNextEncode() {
brandtre78d2662017-01-16 05:57:16 -0800501 rtc::CritScope lock(&local_crit_sect_);
perkjfa10b552016-10-02 23:45:26 -0700502 block_next_encode_ = true;
503 }
504
kthelgason876222f2016-11-29 01:44:11 -0800505 VideoEncoder::ScalingSettings GetScalingSettings() const override {
kthelgason2fc52542017-03-03 00:24:41 -0800506 rtc::CritScope lock(&local_crit_sect_);
kthelgasonad9010c2017-02-14 00:46:51 -0800507 if (quality_scaling_)
Niels Möller225c7872018-02-22 15:03:53 +0100508 return VideoEncoder::ScalingSettings(1, 2, kMinPixelsPerFrame);
509 return VideoEncoder::ScalingSettings::kOff;
kthelgason876222f2016-11-29 01:44:11 -0800510 }
511
perkjfa10b552016-10-02 23:45:26 -0700512 void ContinueEncode() { continue_encode_event_.Set(); }
513
514 void CheckLastTimeStampsMatch(int64_t ntp_time_ms,
515 uint32_t timestamp) const {
brandtre78d2662017-01-16 05:57:16 -0800516 rtc::CritScope lock(&local_crit_sect_);
perkjfa10b552016-10-02 23:45:26 -0700517 EXPECT_EQ(timestamp_, timestamp);
518 EXPECT_EQ(ntp_time_ms_, ntp_time_ms);
519 }
520
kthelgason2fc52542017-03-03 00:24:41 -0800521 void SetQualityScaling(bool b) {
522 rtc::CritScope lock(&local_crit_sect_);
523 quality_scaling_ = b;
524 }
kthelgasonad9010c2017-02-14 00:46:51 -0800525
sprangfe627f32017-03-29 08:24:59 -0700526 void ForceInitEncodeFailure(bool force_failure) {
527 rtc::CritScope lock(&local_crit_sect_);
528 force_init_encode_failed_ = force_failure;
529 }
530
perkjfa10b552016-10-02 23:45:26 -0700531 private:
perkj26091b12016-09-01 01:17:40 -0700532 int32_t Encode(const VideoFrame& input_image,
533 const CodecSpecificInfo* codec_specific_info,
534 const std::vector<FrameType>* frame_types) override {
535 bool block_encode;
536 {
brandtre78d2662017-01-16 05:57:16 -0800537 rtc::CritScope lock(&local_crit_sect_);
perkj26091b12016-09-01 01:17:40 -0700538 EXPECT_GT(input_image.timestamp(), timestamp_);
539 EXPECT_GT(input_image.ntp_time_ms(), ntp_time_ms_);
540 EXPECT_EQ(input_image.timestamp(), input_image.ntp_time_ms() * 90);
541
542 timestamp_ = input_image.timestamp();
543 ntp_time_ms_ = input_image.ntp_time_ms();
perkj803d97f2016-11-01 11:45:46 -0700544 last_input_width_ = input_image.width();
545 last_input_height_ = input_image.height();
perkj26091b12016-09-01 01:17:40 -0700546 block_encode = block_next_encode_;
547 block_next_encode_ = false;
548 }
549 int32_t result =
550 FakeEncoder::Encode(input_image, codec_specific_info, frame_types);
551 if (block_encode)
perkja49cbd32016-09-16 07:53:41 -0700552 EXPECT_TRUE(continue_encode_event_.Wait(kDefaultTimeoutMs));
perkj26091b12016-09-01 01:17:40 -0700553 return result;
554 }
555
sprangfe627f32017-03-29 08:24:59 -0700556 int32_t InitEncode(const VideoCodec* config,
557 int32_t number_of_cores,
558 size_t max_payload_size) override {
559 int res =
560 FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
561 rtc::CritScope lock(&local_crit_sect_);
Erik Språng82fad3d2018-03-21 09:57:23 +0100562 if (config->codecType == kVideoCodecVP8) {
sprangfe627f32017-03-29 08:24:59 -0700563 // Simulate setting up temporal layers, in order to validate the life
564 // cycle of these objects.
565 int num_streams = std::max<int>(1, config->numberOfSimulcastStreams);
sprangfe627f32017-03-29 08:24:59 -0700566 for (int i = 0; i < num_streams; ++i) {
567 allocated_temporal_layers_.emplace_back(
Niels Möller150dcb02018-03-27 14:16:55 +0200568 TemporalLayers::CreateTemporalLayers(*config, i));
sprangfe627f32017-03-29 08:24:59 -0700569 }
570 }
571 if (force_init_encode_failed_)
572 return -1;
573 return res;
574 }
575
brandtre78d2662017-01-16 05:57:16 -0800576 rtc::CriticalSection local_crit_sect_;
danilchapa37de392017-09-09 04:17:22 -0700577 bool block_next_encode_ RTC_GUARDED_BY(local_crit_sect_) = false;
perkj26091b12016-09-01 01:17:40 -0700578 rtc::Event continue_encode_event_;
danilchapa37de392017-09-09 04:17:22 -0700579 uint32_t timestamp_ RTC_GUARDED_BY(local_crit_sect_) = 0;
580 int64_t ntp_time_ms_ RTC_GUARDED_BY(local_crit_sect_) = 0;
581 int last_input_width_ RTC_GUARDED_BY(local_crit_sect_) = 0;
582 int last_input_height_ RTC_GUARDED_BY(local_crit_sect_) = 0;
583 bool quality_scaling_ RTC_GUARDED_BY(local_crit_sect_) = true;
sprangfe627f32017-03-29 08:24:59 -0700584 std::vector<std::unique_ptr<TemporalLayers>> allocated_temporal_layers_
danilchapa37de392017-09-09 04:17:22 -0700585 RTC_GUARDED_BY(local_crit_sect_);
586 bool force_init_encode_failed_ RTC_GUARDED_BY(local_crit_sect_) = false;
perkj26091b12016-09-01 01:17:40 -0700587 };
588
mflodmancc3d4422017-08-03 08:27:51 -0700589 class TestSink : public VideoStreamEncoder::EncoderSink {
perkj26091b12016-09-01 01:17:40 -0700590 public:
591 explicit TestSink(TestEncoder* test_encoder)
592 : test_encoder_(test_encoder), encoded_frame_event_(false, false) {}
593
perkj26091b12016-09-01 01:17:40 -0700594 void WaitForEncodedFrame(int64_t expected_ntp_time) {
sprang4847ae62017-06-27 07:06:52 -0700595 EXPECT_TRUE(
596 TimedWaitForEncodedFrame(expected_ntp_time, kDefaultTimeoutMs));
597 }
598
599 bool TimedWaitForEncodedFrame(int64_t expected_ntp_time,
600 int64_t timeout_ms) {
perkj26091b12016-09-01 01:17:40 -0700601 uint32_t timestamp = 0;
sprang4847ae62017-06-27 07:06:52 -0700602 if (!encoded_frame_event_.Wait(timeout_ms))
603 return false;
perkj26091b12016-09-01 01:17:40 -0700604 {
605 rtc::CritScope lock(&crit_);
sprangb1ca0732017-02-01 08:38:12 -0800606 timestamp = last_timestamp_;
perkj26091b12016-09-01 01:17:40 -0700607 }
608 test_encoder_->CheckLastTimeStampsMatch(expected_ntp_time, timestamp);
sprang4847ae62017-06-27 07:06:52 -0700609 return true;
perkj26091b12016-09-01 01:17:40 -0700610 }
611
sprangb1ca0732017-02-01 08:38:12 -0800612 void WaitForEncodedFrame(uint32_t expected_width,
613 uint32_t expected_height) {
sprangc5d62e22017-04-02 23:53:04 -0700614 EXPECT_TRUE(encoded_frame_event_.Wait(kDefaultTimeoutMs));
Åsa Perssonc74d8da2017-12-04 14:13:56 +0100615 CheckLastFrameSizeMatches(expected_width, expected_height);
sprangc5d62e22017-04-02 23:53:04 -0700616 }
617
Åsa Perssonc74d8da2017-12-04 14:13:56 +0100618 void CheckLastFrameSizeMatches(uint32_t expected_width,
sprangc5d62e22017-04-02 23:53:04 -0700619 uint32_t expected_height) {
sprangb1ca0732017-02-01 08:38:12 -0800620 uint32_t width = 0;
621 uint32_t height = 0;
sprangb1ca0732017-02-01 08:38:12 -0800622 {
623 rtc::CritScope lock(&crit_);
624 width = last_width_;
625 height = last_height_;
626 }
627 EXPECT_EQ(expected_height, height);
628 EXPECT_EQ(expected_width, width);
629 }
630
kthelgason2fc52542017-03-03 00:24:41 -0800631 void ExpectDroppedFrame() { EXPECT_FALSE(encoded_frame_event_.Wait(100)); }
kthelgason2bc68642017-02-07 07:02:22 -0800632
sprangc5d62e22017-04-02 23:53:04 -0700633 bool WaitForFrame(int64_t timeout_ms) {
634 return encoded_frame_event_.Wait(timeout_ms);
635 }
636
perkj26091b12016-09-01 01:17:40 -0700637 void SetExpectNoFrames() {
638 rtc::CritScope lock(&crit_);
639 expect_frames_ = false;
640 }
641
asaperssonfab67072017-04-04 05:51:49 -0700642 int number_of_reconfigurations() const {
Per512ecb32016-09-23 15:52:06 +0200643 rtc::CritScope lock(&crit_);
644 return number_of_reconfigurations_;
645 }
646
asaperssonfab67072017-04-04 05:51:49 -0700647 int last_min_transmit_bitrate() const {
Per512ecb32016-09-23 15:52:06 +0200648 rtc::CritScope lock(&crit_);
649 return min_transmit_bitrate_bps_;
650 }
651
perkj26091b12016-09-01 01:17:40 -0700652 private:
sergeyu2cb155a2016-11-04 11:39:29 -0700653 Result OnEncodedImage(
654 const EncodedImage& encoded_image,
655 const CodecSpecificInfo* codec_specific_info,
656 const RTPFragmentationHeader* fragmentation) override {
Per512ecb32016-09-23 15:52:06 +0200657 rtc::CritScope lock(&crit_);
658 EXPECT_TRUE(expect_frames_);
Niels Möller23775882018-08-16 10:24:12 +0200659 last_timestamp_ = encoded_image.Timestamp();
sprangb1ca0732017-02-01 08:38:12 -0800660 last_width_ = encoded_image._encodedWidth;
661 last_height_ = encoded_image._encodedHeight;
Per512ecb32016-09-23 15:52:06 +0200662 encoded_frame_event_.Set();
sprangb1ca0732017-02-01 08:38:12 -0800663 return Result(Result::OK, last_timestamp_);
Per512ecb32016-09-23 15:52:06 +0200664 }
665
666 void OnEncoderConfigurationChanged(std::vector<VideoStream> streams,
667 int min_transmit_bitrate_bps) override {
668 rtc::CriticalSection crit_;
669 ++number_of_reconfigurations_;
670 min_transmit_bitrate_bps_ = min_transmit_bitrate_bps;
671 }
672
perkj26091b12016-09-01 01:17:40 -0700673 rtc::CriticalSection crit_;
674 TestEncoder* test_encoder_;
675 rtc::Event encoded_frame_event_;
sprangb1ca0732017-02-01 08:38:12 -0800676 uint32_t last_timestamp_ = 0;
677 uint32_t last_height_ = 0;
678 uint32_t last_width_ = 0;
perkj26091b12016-09-01 01:17:40 -0700679 bool expect_frames_ = true;
Per512ecb32016-09-23 15:52:06 +0200680 int number_of_reconfigurations_ = 0;
681 int min_transmit_bitrate_bps_ = 0;
perkj26091b12016-09-01 01:17:40 -0700682 };
683
684 VideoSendStream::Config video_send_config_;
Erik Språng08127a92016-11-16 16:41:30 +0100685 VideoEncoderConfig video_encoder_config_;
Per512ecb32016-09-23 15:52:06 +0200686 int codec_width_;
687 int codec_height_;
sprang4847ae62017-06-27 07:06:52 -0700688 int max_framerate_;
perkj26091b12016-09-01 01:17:40 -0700689 TestEncoder fake_encoder_;
Niels Möller4db138e2018-04-19 09:04:13 +0200690 test::EncoderProxyFactory encoder_factory_;
sprangc5d62e22017-04-02 23:53:04 -0700691 std::unique_ptr<MockableSendStatisticsProxy> stats_proxy_;
perkj26091b12016-09-01 01:17:40 -0700692 TestSink sink_;
sprangb1ca0732017-02-01 08:38:12 -0800693 AdaptingFrameForwarder video_source_;
mflodmancc3d4422017-08-03 08:27:51 -0700694 std::unique_ptr<VideoStreamEncoderUnderTest> video_stream_encoder_;
sprang4847ae62017-06-27 07:06:52 -0700695 rtc::ScopedFakeClock fake_clock_;
perkj26091b12016-09-01 01:17:40 -0700696};
697
mflodmancc3d4422017-08-03 08:27:51 -0700698TEST_F(VideoStreamEncoderTest, EncodeOneFrame) {
699 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkj26091b12016-09-01 01:17:40 -0700700 rtc::Event frame_destroyed_event(false, false);
perkja49cbd32016-09-16 07:53:41 -0700701 video_source_.IncomingCapturedFrame(CreateFrame(1, &frame_destroyed_event));
sprang4847ae62017-06-27 07:06:52 -0700702 WaitForEncodedFrame(1);
perkja49cbd32016-09-16 07:53:41 -0700703 EXPECT_TRUE(frame_destroyed_event.Wait(kDefaultTimeoutMs));
mflodmancc3d4422017-08-03 08:27:51 -0700704 video_stream_encoder_->Stop();
perkj26091b12016-09-01 01:17:40 -0700705}
706
mflodmancc3d4422017-08-03 08:27:51 -0700707TEST_F(VideoStreamEncoderTest, DropsFramesBeforeFirstOnBitrateUpdated) {
perkj26091b12016-09-01 01:17:40 -0700708 // Dropped since no target bitrate has been set.
709 rtc::Event frame_destroyed_event(false, false);
Sebastian Janssona3177052018-04-10 13:05:49 +0200710 // The encoder will cache up to one frame for a short duration. Adding two
711 // frames means that the first frame will be dropped and the second frame will
712 // be sent when the encoder is enabled.
perkja49cbd32016-09-16 07:53:41 -0700713 video_source_.IncomingCapturedFrame(CreateFrame(1, &frame_destroyed_event));
Sebastian Janssona3177052018-04-10 13:05:49 +0200714 video_source_.IncomingCapturedFrame(CreateFrame(2, nullptr));
perkja49cbd32016-09-16 07:53:41 -0700715 EXPECT_TRUE(frame_destroyed_event.Wait(kDefaultTimeoutMs));
perkj26091b12016-09-01 01:17:40 -0700716
mflodmancc3d4422017-08-03 08:27:51 -0700717 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkj26091b12016-09-01 01:17:40 -0700718
Sebastian Janssona3177052018-04-10 13:05:49 +0200719 // The pending frame should be received.
sprang4847ae62017-06-27 07:06:52 -0700720 WaitForEncodedFrame(2);
Sebastian Janssona3177052018-04-10 13:05:49 +0200721 video_source_.IncomingCapturedFrame(CreateFrame(3, nullptr));
722
723 WaitForEncodedFrame(3);
mflodmancc3d4422017-08-03 08:27:51 -0700724 video_stream_encoder_->Stop();
perkj26091b12016-09-01 01:17:40 -0700725}
726
mflodmancc3d4422017-08-03 08:27:51 -0700727TEST_F(VideoStreamEncoderTest, DropsFramesWhenRateSetToZero) {
728 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkja49cbd32016-09-16 07:53:41 -0700729 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700730 WaitForEncodedFrame(1);
perkj26091b12016-09-01 01:17:40 -0700731
mflodmancc3d4422017-08-03 08:27:51 -0700732 video_stream_encoder_->OnBitrateUpdated(0, 0, 0);
Sebastian Janssona3177052018-04-10 13:05:49 +0200733 // The encoder will cache up to one frame for a short duration. Adding two
734 // frames means that the first frame will be dropped and the second frame will
735 // be sent when the encoder is resumed.
perkja49cbd32016-09-16 07:53:41 -0700736 video_source_.IncomingCapturedFrame(CreateFrame(2, nullptr));
Sebastian Janssona3177052018-04-10 13:05:49 +0200737 video_source_.IncomingCapturedFrame(CreateFrame(3, nullptr));
perkj26091b12016-09-01 01:17:40 -0700738
mflodmancc3d4422017-08-03 08:27:51 -0700739 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
sprang4847ae62017-06-27 07:06:52 -0700740 WaitForEncodedFrame(3);
Sebastian Janssona3177052018-04-10 13:05:49 +0200741 video_source_.IncomingCapturedFrame(CreateFrame(4, nullptr));
742 WaitForEncodedFrame(4);
mflodmancc3d4422017-08-03 08:27:51 -0700743 video_stream_encoder_->Stop();
perkj26091b12016-09-01 01:17:40 -0700744}
745
mflodmancc3d4422017-08-03 08:27:51 -0700746TEST_F(VideoStreamEncoderTest, DropsFramesWithSameOrOldNtpTimestamp) {
747 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkja49cbd32016-09-16 07:53:41 -0700748 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700749 WaitForEncodedFrame(1);
perkj26091b12016-09-01 01:17:40 -0700750
751 // This frame will be dropped since it has the same ntp timestamp.
perkja49cbd32016-09-16 07:53:41 -0700752 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
perkj26091b12016-09-01 01:17:40 -0700753
perkja49cbd32016-09-16 07:53:41 -0700754 video_source_.IncomingCapturedFrame(CreateFrame(2, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700755 WaitForEncodedFrame(2);
mflodmancc3d4422017-08-03 08:27:51 -0700756 video_stream_encoder_->Stop();
perkj26091b12016-09-01 01:17:40 -0700757}
758
mflodmancc3d4422017-08-03 08:27:51 -0700759TEST_F(VideoStreamEncoderTest, DropsFrameAfterStop) {
760 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkj26091b12016-09-01 01:17:40 -0700761
perkja49cbd32016-09-16 07:53:41 -0700762 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700763 WaitForEncodedFrame(1);
perkj26091b12016-09-01 01:17:40 -0700764
mflodmancc3d4422017-08-03 08:27:51 -0700765 video_stream_encoder_->Stop();
perkj26091b12016-09-01 01:17:40 -0700766 sink_.SetExpectNoFrames();
767 rtc::Event frame_destroyed_event(false, false);
perkja49cbd32016-09-16 07:53:41 -0700768 video_source_.IncomingCapturedFrame(CreateFrame(2, &frame_destroyed_event));
769 EXPECT_TRUE(frame_destroyed_event.Wait(kDefaultTimeoutMs));
perkj26091b12016-09-01 01:17:40 -0700770}
771
mflodmancc3d4422017-08-03 08:27:51 -0700772TEST_F(VideoStreamEncoderTest, DropsPendingFramesOnSlowEncode) {
773 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkj26091b12016-09-01 01:17:40 -0700774
775 fake_encoder_.BlockNextEncode();
perkja49cbd32016-09-16 07:53:41 -0700776 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700777 WaitForEncodedFrame(1);
perkj26091b12016-09-01 01:17:40 -0700778 // Here, the encoder thread will be blocked in the TestEncoder waiting for a
779 // call to ContinueEncode.
perkja49cbd32016-09-16 07:53:41 -0700780 video_source_.IncomingCapturedFrame(CreateFrame(2, nullptr));
781 video_source_.IncomingCapturedFrame(CreateFrame(3, nullptr));
perkj26091b12016-09-01 01:17:40 -0700782 fake_encoder_.ContinueEncode();
sprang4847ae62017-06-27 07:06:52 -0700783 WaitForEncodedFrame(3);
perkj26091b12016-09-01 01:17:40 -0700784
mflodmancc3d4422017-08-03 08:27:51 -0700785 video_stream_encoder_->Stop();
perkj26091b12016-09-01 01:17:40 -0700786}
787
mflodmancc3d4422017-08-03 08:27:51 -0700788TEST_F(VideoStreamEncoderTest,
789 ConfigureEncoderTriggersOnEncoderConfigurationChanged) {
790 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
Per21d45d22016-10-30 21:37:57 +0100791 EXPECT_EQ(0, sink_.number_of_reconfigurations());
Per512ecb32016-09-23 15:52:06 +0200792
793 // Capture a frame and wait for it to synchronize with the encoder thread.
Perf8c5f2b2016-09-23 16:24:55 +0200794 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700795 WaitForEncodedFrame(1);
Per21d45d22016-10-30 21:37:57 +0100796 // The encoder will have been configured once when the first frame is
797 // received.
798 EXPECT_EQ(1, sink_.number_of_reconfigurations());
Per512ecb32016-09-23 15:52:06 +0200799
800 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +0200801 test::FillEncoderConfiguration(kVideoCodecVP8, 1, &video_encoder_config);
Per512ecb32016-09-23 15:52:06 +0200802 video_encoder_config.min_transmit_bitrate_bps = 9999;
mflodmancc3d4422017-08-03 08:27:51 -0700803 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +0200804 kMaxPayloadLength);
Per512ecb32016-09-23 15:52:06 +0200805
806 // Capture a frame and wait for it to synchronize with the encoder thread.
Perf8c5f2b2016-09-23 16:24:55 +0200807 video_source_.IncomingCapturedFrame(CreateFrame(2, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700808 WaitForEncodedFrame(2);
Per21d45d22016-10-30 21:37:57 +0100809 EXPECT_EQ(2, sink_.number_of_reconfigurations());
perkj3b703ed2016-09-29 23:25:40 -0700810 EXPECT_EQ(9999, sink_.last_min_transmit_bitrate());
perkj26105b42016-09-29 22:39:10 -0700811
mflodmancc3d4422017-08-03 08:27:51 -0700812 video_stream_encoder_->Stop();
perkj26105b42016-09-29 22:39:10 -0700813}
814
mflodmancc3d4422017-08-03 08:27:51 -0700815TEST_F(VideoStreamEncoderTest, FrameResolutionChangeReconfigureEncoder) {
816 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkjfa10b552016-10-02 23:45:26 -0700817
818 // Capture a frame and wait for it to synchronize with the encoder thread.
819 video_source_.IncomingCapturedFrame(CreateFrame(1, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700820 WaitForEncodedFrame(1);
Per21d45d22016-10-30 21:37:57 +0100821 // The encoder will have been configured once.
822 EXPECT_EQ(1, sink_.number_of_reconfigurations());
perkjfa10b552016-10-02 23:45:26 -0700823 EXPECT_EQ(codec_width_, fake_encoder_.codec_config().width);
824 EXPECT_EQ(codec_height_, fake_encoder_.codec_config().height);
825
826 codec_width_ *= 2;
827 codec_height_ *= 2;
828 // Capture a frame with a higher resolution and wait for it to synchronize
829 // with the encoder thread.
830 video_source_.IncomingCapturedFrame(CreateFrame(2, nullptr));
sprang4847ae62017-06-27 07:06:52 -0700831 WaitForEncodedFrame(2);
perkjfa10b552016-10-02 23:45:26 -0700832 EXPECT_EQ(codec_width_, fake_encoder_.codec_config().width);
833 EXPECT_EQ(codec_height_, fake_encoder_.codec_config().height);
Per21d45d22016-10-30 21:37:57 +0100834 EXPECT_EQ(2, sink_.number_of_reconfigurations());
perkjfa10b552016-10-02 23:45:26 -0700835
mflodmancc3d4422017-08-03 08:27:51 -0700836 video_stream_encoder_->Stop();
perkjfa10b552016-10-02 23:45:26 -0700837}
838
mflodmancc3d4422017-08-03 08:27:51 -0700839TEST_F(VideoStreamEncoderTest, SwitchSourceDeregisterEncoderAsSink) {
perkj803d97f2016-11-01 11:45:46 -0700840 EXPECT_TRUE(video_source_.has_sinks());
841 test::FrameForwarder new_video_source;
mflodmancc3d4422017-08-03 08:27:51 -0700842 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700843 &new_video_source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
perkj803d97f2016-11-01 11:45:46 -0700844 EXPECT_FALSE(video_source_.has_sinks());
845 EXPECT_TRUE(new_video_source.has_sinks());
846
mflodmancc3d4422017-08-03 08:27:51 -0700847 video_stream_encoder_->Stop();
perkj803d97f2016-11-01 11:45:46 -0700848}
849
mflodmancc3d4422017-08-03 08:27:51 -0700850TEST_F(VideoStreamEncoderTest, SinkWantsRotationApplied) {
perkj803d97f2016-11-01 11:45:46 -0700851 EXPECT_FALSE(video_source_.sink_wants().rotation_applied);
mflodmancc3d4422017-08-03 08:27:51 -0700852 video_stream_encoder_->SetSink(&sink_, true /*rotation_applied*/);
perkj803d97f2016-11-01 11:45:46 -0700853 EXPECT_TRUE(video_source_.sink_wants().rotation_applied);
mflodmancc3d4422017-08-03 08:27:51 -0700854 video_stream_encoder_->Stop();
perkj803d97f2016-11-01 11:45:46 -0700855}
856
Jonathan Yubc771b72017-12-08 17:04:29 -0800857TEST_F(VideoStreamEncoderTest, TestCpuDowngrades_BalancedMode) {
858 const int kFramerateFps = 30;
asaperssonf7e294d2017-06-13 23:25:22 -0700859 const int kWidth = 1280;
860 const int kHeight = 720;
Jonathan Yubc771b72017-12-08 17:04:29 -0800861
862 // We rely on the automatic resolution adaptation, but we handle framerate
863 // adaptation manually by mocking the stats proxy.
864 video_source_.set_adaptation_enabled(true);
asaperssonf7e294d2017-06-13 23:25:22 -0700865
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700866 // Enable BALANCED preference, no initial limitation.
Jonathan Yubc771b72017-12-08 17:04:29 -0800867 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700868 video_stream_encoder_->SetSource(&video_source_,
869 webrtc::DegradationPreference::BALANCED);
Jonathan Yubc771b72017-12-08 17:04:29 -0800870 VerifyNoLimitation(video_source_.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -0700871 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -0800872 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
asaperssonf7e294d2017-06-13 23:25:22 -0700873 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
874
Jonathan Yubc771b72017-12-08 17:04:29 -0800875 // Adapt down as far as possible.
876 rtc::VideoSinkWants last_wants;
877 int64_t t = 1;
878 int loop_count = 0;
879 do {
880 ++loop_count;
881 last_wants = video_source_.sink_wants();
882
883 // Simulate the framerate we've been asked to adapt to.
884 const int fps = std::min(kFramerateFps, last_wants.max_framerate_fps);
885 const int frame_interval_ms = rtc::kNumMillisecsPerSec / fps;
886 VideoSendStream::Stats mock_stats = stats_proxy_->GetStats();
887 mock_stats.input_frame_rate = fps;
888 stats_proxy_->SetMockStats(mock_stats);
889
890 video_source_.IncomingCapturedFrame(CreateFrame(t, kWidth, kHeight));
891 sink_.WaitForEncodedFrame(t);
892 t += frame_interval_ms;
893
mflodmancc3d4422017-08-03 08:27:51 -0700894 video_stream_encoder_->TriggerCpuOveruse();
Jonathan Yubc771b72017-12-08 17:04:29 -0800895 VerifyBalancedModeFpsRange(
896 video_source_.sink_wants(),
897 *video_source_.last_sent_width() * *video_source_.last_sent_height());
898 } while (video_source_.sink_wants().max_pixel_count <
899 last_wants.max_pixel_count ||
900 video_source_.sink_wants().max_framerate_fps <
901 last_wants.max_framerate_fps);
asaperssonf7e294d2017-06-13 23:25:22 -0700902
Jonathan Yubc771b72017-12-08 17:04:29 -0800903 // Verify that we've adapted all the way down.
904 stats_proxy_->ResetMockStats();
asaperssonf7e294d2017-06-13 23:25:22 -0700905 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -0800906 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_framerate);
907 EXPECT_EQ(loop_count - 1,
asaperssonf7e294d2017-06-13 23:25:22 -0700908 stats_proxy_->GetStats().number_of_cpu_adapt_changes);
Jonathan Yubc771b72017-12-08 17:04:29 -0800909 EXPECT_EQ(kMinPixelsPerFrame, *video_source_.last_sent_width() *
910 *video_source_.last_sent_height());
911 EXPECT_EQ(kMinBalancedFramerateFps,
912 video_source_.sink_wants().max_framerate_fps);
asaperssonf7e294d2017-06-13 23:25:22 -0700913
Jonathan Yubc771b72017-12-08 17:04:29 -0800914 // Adapt back up the same number of times we adapted down.
915 for (int i = 0; i < loop_count - 1; ++i) {
916 last_wants = video_source_.sink_wants();
917
918 // Simulate the framerate we've been asked to adapt to.
919 const int fps = std::min(kFramerateFps, last_wants.max_framerate_fps);
920 const int frame_interval_ms = rtc::kNumMillisecsPerSec / fps;
921 VideoSendStream::Stats mock_stats = stats_proxy_->GetStats();
922 mock_stats.input_frame_rate = fps;
923 stats_proxy_->SetMockStats(mock_stats);
924
925 video_source_.IncomingCapturedFrame(CreateFrame(t, kWidth, kHeight));
926 sink_.WaitForEncodedFrame(t);
927 t += frame_interval_ms;
928
mflodmancc3d4422017-08-03 08:27:51 -0700929 video_stream_encoder_->TriggerCpuNormalUsage();
Jonathan Yubc771b72017-12-08 17:04:29 -0800930 VerifyBalancedModeFpsRange(
931 video_source_.sink_wants(),
932 *video_source_.last_sent_width() * *video_source_.last_sent_height());
933 EXPECT_TRUE(video_source_.sink_wants().max_pixel_count >
934 last_wants.max_pixel_count ||
935 video_source_.sink_wants().max_framerate_fps >
936 last_wants.max_framerate_fps);
asaperssonf7e294d2017-06-13 23:25:22 -0700937 }
938
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000939 VerifyNoLimitation(video_source_.sink_wants());
Jonathan Yubc771b72017-12-08 17:04:29 -0800940 stats_proxy_->ResetMockStats();
asaperssonf7e294d2017-06-13 23:25:22 -0700941 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -0800942 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
943 EXPECT_EQ((loop_count - 1) * 2,
944 stats_proxy_->GetStats().number_of_cpu_adapt_changes);
asaperssonf7e294d2017-06-13 23:25:22 -0700945
mflodmancc3d4422017-08-03 08:27:51 -0700946 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -0700947}
mflodmancc3d4422017-08-03 08:27:51 -0700948TEST_F(VideoStreamEncoderTest, SinkWantsStoredByDegradationPreference) {
949 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson02465b82017-04-10 01:12:52 -0700950 VerifyNoLimitation(video_source_.sink_wants());
perkj803d97f2016-11-01 11:45:46 -0700951
sprangc5d62e22017-04-02 23:53:04 -0700952 const int kFrameWidth = 1280;
953 const int kFrameHeight = 720;
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000954 const int kFrameIntervalMs = 1000 / 30;
sprangc5d62e22017-04-02 23:53:04 -0700955
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000956 int frame_timestamp = 1;
perkj803d97f2016-11-01 11:45:46 -0700957
kthelgason5e13d412016-12-01 03:59:51 -0800958 video_source_.IncomingCapturedFrame(
sprangc5d62e22017-04-02 23:53:04 -0700959 CreateFrame(frame_timestamp, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -0700960 WaitForEncodedFrame(frame_timestamp);
sprangc5d62e22017-04-02 23:53:04 -0700961 frame_timestamp += kFrameIntervalMs;
962
perkj803d97f2016-11-01 11:45:46 -0700963 // Trigger CPU overuse.
mflodmancc3d4422017-08-03 08:27:51 -0700964 video_stream_encoder_->TriggerCpuOveruse();
perkj803d97f2016-11-01 11:45:46 -0700965 video_source_.IncomingCapturedFrame(
sprangc5d62e22017-04-02 23:53:04 -0700966 CreateFrame(frame_timestamp, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -0700967 WaitForEncodedFrame(frame_timestamp);
sprangc5d62e22017-04-02 23:53:04 -0700968 frame_timestamp += kFrameIntervalMs;
sprang3ea3c772017-03-30 07:23:48 -0700969
asapersson0944a802017-04-07 00:57:58 -0700970 // Default degradation preference is maintain-framerate, so will lower max
sprangc5d62e22017-04-02 23:53:04 -0700971 // wanted resolution.
972 EXPECT_FALSE(video_source_.sink_wants().target_pixel_count);
973 EXPECT_LT(video_source_.sink_wants().max_pixel_count,
974 kFrameWidth * kFrameHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000975 EXPECT_EQ(std::numeric_limits<int>::max(),
976 video_source_.sink_wants().max_framerate_fps);
sprangc5d62e22017-04-02 23:53:04 -0700977
978 // Set new source, switch to maintain-resolution.
perkj803d97f2016-11-01 11:45:46 -0700979 test::FrameForwarder new_video_source;
mflodmancc3d4422017-08-03 08:27:51 -0700980 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -0700981 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
perkj803d97f2016-11-01 11:45:46 -0700982
sprangc5d62e22017-04-02 23:53:04 -0700983 // Initially no degradation registered.
Mirko Bonadei948b7e32018-08-14 07:23:21 +0000984 VerifyNoLimitation(new_video_source.sink_wants());
perkj803d97f2016-11-01 11:45:46 -0700985
sprangc5d62e22017-04-02 23:53:04 -0700986 // Force an input frame rate to be available, or the adaptation call won't
987 // know what framerate to adapt form.
asapersson02465b82017-04-10 01:12:52 -0700988 const int kInputFps = 30;
sprangc5d62e22017-04-02 23:53:04 -0700989 VideoSendStream::Stats stats = stats_proxy_->GetStats();
asapersson02465b82017-04-10 01:12:52 -0700990 stats.input_frame_rate = kInputFps;
sprangc5d62e22017-04-02 23:53:04 -0700991 stats_proxy_->SetMockStats(stats);
992
mflodmancc3d4422017-08-03 08:27:51 -0700993 video_stream_encoder_->TriggerCpuOveruse();
perkj803d97f2016-11-01 11:45:46 -0700994 new_video_source.IncomingCapturedFrame(
sprangc5d62e22017-04-02 23:53:04 -0700995 CreateFrame(frame_timestamp, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -0700996 WaitForEncodedFrame(frame_timestamp);
sprangc5d62e22017-04-02 23:53:04 -0700997 frame_timestamp += kFrameIntervalMs;
998
999 // Some framerate constraint should be set.
sprang84a37592017-02-10 07:04:27 -08001000 EXPECT_FALSE(new_video_source.sink_wants().target_pixel_count);
sprangc5d62e22017-04-02 23:53:04 -07001001 EXPECT_EQ(std::numeric_limits<int>::max(),
1002 new_video_source.sink_wants().max_pixel_count);
asapersson02465b82017-04-10 01:12:52 -07001003 EXPECT_LT(new_video_source.sink_wants().max_framerate_fps, kInputFps);
sprangc5d62e22017-04-02 23:53:04 -07001004
asapersson02465b82017-04-10 01:12:52 -07001005 // Turn off degradation completely.
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001006 video_stream_encoder_->SetSource(&new_video_source,
1007 webrtc::DegradationPreference::DISABLED);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001008 VerifyNoLimitation(new_video_source.sink_wants());
sprangc5d62e22017-04-02 23:53:04 -07001009
mflodmancc3d4422017-08-03 08:27:51 -07001010 video_stream_encoder_->TriggerCpuOveruse();
sprangc5d62e22017-04-02 23:53:04 -07001011 new_video_source.IncomingCapturedFrame(
1012 CreateFrame(frame_timestamp, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07001013 WaitForEncodedFrame(frame_timestamp);
sprangc5d62e22017-04-02 23:53:04 -07001014 frame_timestamp += kFrameIntervalMs;
1015
1016 // Still no degradation.
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001017 VerifyNoLimitation(new_video_source.sink_wants());
perkj803d97f2016-11-01 11:45:46 -07001018
1019 // Calling SetSource with resolution scaling enabled apply the old SinkWants.
mflodmancc3d4422017-08-03 08:27:51 -07001020 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001021 &new_video_source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
sprangc5d62e22017-04-02 23:53:04 -07001022 EXPECT_LT(new_video_source.sink_wants().max_pixel_count,
1023 kFrameWidth * kFrameHeight);
sprang84a37592017-02-10 07:04:27 -08001024 EXPECT_FALSE(new_video_source.sink_wants().target_pixel_count);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001025 EXPECT_EQ(std::numeric_limits<int>::max(),
1026 new_video_source.sink_wants().max_framerate_fps);
sprangc5d62e22017-04-02 23:53:04 -07001027
1028 // Calling SetSource with framerate scaling enabled apply the old SinkWants.
mflodmancc3d4422017-08-03 08:27:51 -07001029 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001030 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
sprangc5d62e22017-04-02 23:53:04 -07001031 EXPECT_FALSE(new_video_source.sink_wants().target_pixel_count);
1032 EXPECT_EQ(std::numeric_limits<int>::max(),
1033 new_video_source.sink_wants().max_pixel_count);
asapersson02465b82017-04-10 01:12:52 -07001034 EXPECT_LT(new_video_source.sink_wants().max_framerate_fps, kInputFps);
perkj803d97f2016-11-01 11:45:46 -07001035
mflodmancc3d4422017-08-03 08:27:51 -07001036 video_stream_encoder_->Stop();
perkj803d97f2016-11-01 11:45:46 -07001037}
1038
mflodmancc3d4422017-08-03 08:27:51 -07001039TEST_F(VideoStreamEncoderTest, StatsTracksQualityAdaptationStats) {
1040 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkj803d97f2016-11-01 11:45:46 -07001041
asaperssonfab67072017-04-04 05:51:49 -07001042 const int kWidth = 1280;
1043 const int kHeight = 720;
asaperssonfab67072017-04-04 05:51:49 -07001044 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001045 WaitForEncodedFrame(1);
asaperssonfab67072017-04-04 05:51:49 -07001046 VideoSendStream::Stats stats = stats_proxy_->GetStats();
1047 EXPECT_FALSE(stats.bw_limited_resolution);
1048 EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
1049
1050 // Trigger adapt down.
mflodmancc3d4422017-08-03 08:27:51 -07001051 video_stream_encoder_->TriggerQualityLow();
asaperssonfab67072017-04-04 05:51:49 -07001052 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001053 WaitForEncodedFrame(2);
asaperssonfab67072017-04-04 05:51:49 -07001054
1055 stats = stats_proxy_->GetStats();
1056 EXPECT_TRUE(stats.bw_limited_resolution);
1057 EXPECT_EQ(1, stats.number_of_quality_adapt_changes);
1058
1059 // Trigger adapt up.
mflodmancc3d4422017-08-03 08:27:51 -07001060 video_stream_encoder_->TriggerQualityHigh();
asaperssonfab67072017-04-04 05:51:49 -07001061 video_source_.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001062 WaitForEncodedFrame(3);
asaperssonfab67072017-04-04 05:51:49 -07001063
1064 stats = stats_proxy_->GetStats();
1065 EXPECT_FALSE(stats.bw_limited_resolution);
1066 EXPECT_EQ(2, stats.number_of_quality_adapt_changes);
1067 EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
1068
mflodmancc3d4422017-08-03 08:27:51 -07001069 video_stream_encoder_->Stop();
asaperssonfab67072017-04-04 05:51:49 -07001070}
1071
mflodmancc3d4422017-08-03 08:27:51 -07001072TEST_F(VideoStreamEncoderTest, StatsTracksCpuAdaptationStats) {
1073 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonfab67072017-04-04 05:51:49 -07001074
1075 const int kWidth = 1280;
1076 const int kHeight = 720;
asaperssonfab67072017-04-04 05:51:49 -07001077 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001078 WaitForEncodedFrame(1);
perkj803d97f2016-11-01 11:45:46 -07001079 VideoSendStream::Stats stats = stats_proxy_->GetStats();
1080 EXPECT_FALSE(stats.cpu_limited_resolution);
1081 EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
1082
1083 // Trigger CPU overuse.
mflodmancc3d4422017-08-03 08:27:51 -07001084 video_stream_encoder_->TriggerCpuOveruse();
asaperssonfab67072017-04-04 05:51:49 -07001085 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001086 WaitForEncodedFrame(2);
perkj803d97f2016-11-01 11:45:46 -07001087
1088 stats = stats_proxy_->GetStats();
1089 EXPECT_TRUE(stats.cpu_limited_resolution);
1090 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1091
1092 // Trigger CPU normal use.
mflodmancc3d4422017-08-03 08:27:51 -07001093 video_stream_encoder_->TriggerCpuNormalUsage();
asaperssonfab67072017-04-04 05:51:49 -07001094 video_source_.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001095 WaitForEncodedFrame(3);
perkj803d97f2016-11-01 11:45:46 -07001096
1097 stats = stats_proxy_->GetStats();
1098 EXPECT_FALSE(stats.cpu_limited_resolution);
1099 EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
asaperssonfab67072017-04-04 05:51:49 -07001100 EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
perkj803d97f2016-11-01 11:45:46 -07001101
mflodmancc3d4422017-08-03 08:27:51 -07001102 video_stream_encoder_->Stop();
perkj803d97f2016-11-01 11:45:46 -07001103}
1104
mflodmancc3d4422017-08-03 08:27:51 -07001105TEST_F(VideoStreamEncoderTest, SwitchingSourceKeepsCpuAdaptation) {
1106 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
kthelgason876222f2016-11-29 01:44:11 -08001107
asaperssonfab67072017-04-04 05:51:49 -07001108 const int kWidth = 1280;
1109 const int kHeight = 720;
1110 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001111 WaitForEncodedFrame(1);
kthelgason876222f2016-11-29 01:44:11 -08001112 VideoSendStream::Stats stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001113 EXPECT_FALSE(stats.bw_limited_resolution);
kthelgason876222f2016-11-29 01:44:11 -08001114 EXPECT_FALSE(stats.cpu_limited_resolution);
1115 EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
1116
asaperssonfab67072017-04-04 05:51:49 -07001117 // Trigger CPU overuse.
mflodmancc3d4422017-08-03 08:27:51 -07001118 video_stream_encoder_->TriggerCpuOveruse();
asaperssonfab67072017-04-04 05:51:49 -07001119 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001120 WaitForEncodedFrame(2);
kthelgason876222f2016-11-29 01:44:11 -08001121 stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001122 EXPECT_FALSE(stats.bw_limited_resolution);
kthelgason876222f2016-11-29 01:44:11 -08001123 EXPECT_TRUE(stats.cpu_limited_resolution);
1124 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1125
1126 // Set new source with adaptation still enabled.
1127 test::FrameForwarder new_video_source;
mflodmancc3d4422017-08-03 08:27:51 -07001128 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001129 &new_video_source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
kthelgason876222f2016-11-29 01:44:11 -08001130
asaperssonfab67072017-04-04 05:51:49 -07001131 new_video_source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001132 WaitForEncodedFrame(3);
kthelgason876222f2016-11-29 01:44:11 -08001133 stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001134 EXPECT_FALSE(stats.bw_limited_resolution);
kthelgason876222f2016-11-29 01:44:11 -08001135 EXPECT_TRUE(stats.cpu_limited_resolution);
1136 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1137
1138 // Set adaptation disabled.
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001139 video_stream_encoder_->SetSource(&new_video_source,
1140 webrtc::DegradationPreference::DISABLED);
kthelgason876222f2016-11-29 01:44:11 -08001141
asaperssonfab67072017-04-04 05:51:49 -07001142 new_video_source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001143 WaitForEncodedFrame(4);
kthelgason876222f2016-11-29 01:44:11 -08001144 stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001145 EXPECT_FALSE(stats.bw_limited_resolution);
kthelgason876222f2016-11-29 01:44:11 -08001146 EXPECT_FALSE(stats.cpu_limited_resolution);
1147 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1148
1149 // Set adaptation back to enabled.
mflodmancc3d4422017-08-03 08:27:51 -07001150 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001151 &new_video_source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
kthelgason876222f2016-11-29 01:44:11 -08001152
asaperssonfab67072017-04-04 05:51:49 -07001153 new_video_source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001154 WaitForEncodedFrame(5);
kthelgason876222f2016-11-29 01:44:11 -08001155 stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001156 EXPECT_FALSE(stats.bw_limited_resolution);
kthelgason876222f2016-11-29 01:44:11 -08001157 EXPECT_TRUE(stats.cpu_limited_resolution);
1158 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1159
asaperssonfab67072017-04-04 05:51:49 -07001160 // Trigger CPU normal use.
mflodmancc3d4422017-08-03 08:27:51 -07001161 video_stream_encoder_->TriggerCpuNormalUsage();
asaperssonfab67072017-04-04 05:51:49 -07001162 new_video_source.IncomingCapturedFrame(CreateFrame(6, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001163 WaitForEncodedFrame(6);
kthelgason876222f2016-11-29 01:44:11 -08001164 stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001165 EXPECT_FALSE(stats.bw_limited_resolution);
kthelgason876222f2016-11-29 01:44:11 -08001166 EXPECT_FALSE(stats.cpu_limited_resolution);
1167 EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
asapersson02465b82017-04-10 01:12:52 -07001168 EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
kthelgason876222f2016-11-29 01:44:11 -08001169
mflodmancc3d4422017-08-03 08:27:51 -07001170 video_stream_encoder_->Stop();
kthelgason876222f2016-11-29 01:44:11 -08001171}
1172
mflodmancc3d4422017-08-03 08:27:51 -07001173TEST_F(VideoStreamEncoderTest, SwitchingSourceKeepsQualityAdaptation) {
1174 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
kthelgason876222f2016-11-29 01:44:11 -08001175
asaperssonfab67072017-04-04 05:51:49 -07001176 const int kWidth = 1280;
1177 const int kHeight = 720;
1178 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001179 WaitForEncodedFrame(1);
kthelgason876222f2016-11-29 01:44:11 -08001180 VideoSendStream::Stats stats = stats_proxy_->GetStats();
kthelgason876222f2016-11-29 01:44:11 -08001181 EXPECT_FALSE(stats.bw_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001182 EXPECT_FALSE(stats.bw_limited_framerate);
asaperssonfab67072017-04-04 05:51:49 -07001183 EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
kthelgason876222f2016-11-29 01:44:11 -08001184
1185 // Set new source with adaptation still enabled.
1186 test::FrameForwarder new_video_source;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001187 video_stream_encoder_->SetSource(&new_video_source,
1188 webrtc::DegradationPreference::BALANCED);
kthelgason876222f2016-11-29 01:44:11 -08001189
asaperssonfab67072017-04-04 05:51:49 -07001190 new_video_source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001191 WaitForEncodedFrame(2);
kthelgason876222f2016-11-29 01:44:11 -08001192 stats = stats_proxy_->GetStats();
kthelgason876222f2016-11-29 01:44:11 -08001193 EXPECT_FALSE(stats.bw_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001194 EXPECT_FALSE(stats.bw_limited_framerate);
asaperssonfab67072017-04-04 05:51:49 -07001195 EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
kthelgason876222f2016-11-29 01:44:11 -08001196
asaperssonfab67072017-04-04 05:51:49 -07001197 // Trigger adapt down.
mflodmancc3d4422017-08-03 08:27:51 -07001198 video_stream_encoder_->TriggerQualityLow();
asaperssonfab67072017-04-04 05:51:49 -07001199 new_video_source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001200 WaitForEncodedFrame(3);
kthelgason876222f2016-11-29 01:44:11 -08001201 stats = stats_proxy_->GetStats();
kthelgason876222f2016-11-29 01:44:11 -08001202 EXPECT_TRUE(stats.bw_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001203 EXPECT_FALSE(stats.bw_limited_framerate);
asaperssonfab67072017-04-04 05:51:49 -07001204 EXPECT_EQ(1, stats.number_of_quality_adapt_changes);
kthelgason876222f2016-11-29 01:44:11 -08001205
asaperssonfab67072017-04-04 05:51:49 -07001206 // Set new source with adaptation still enabled.
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001207 video_stream_encoder_->SetSource(&new_video_source,
1208 webrtc::DegradationPreference::BALANCED);
kthelgason876222f2016-11-29 01:44:11 -08001209
asaperssonfab67072017-04-04 05:51:49 -07001210 new_video_source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001211 WaitForEncodedFrame(4);
kthelgason876222f2016-11-29 01:44:11 -08001212 stats = stats_proxy_->GetStats();
kthelgason876222f2016-11-29 01:44:11 -08001213 EXPECT_TRUE(stats.bw_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001214 EXPECT_FALSE(stats.bw_limited_framerate);
asaperssonfab67072017-04-04 05:51:49 -07001215 EXPECT_EQ(1, stats.number_of_quality_adapt_changes);
kthelgason876222f2016-11-29 01:44:11 -08001216
asapersson02465b82017-04-10 01:12:52 -07001217 // Disable resolution scaling.
mflodmancc3d4422017-08-03 08:27:51 -07001218 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001219 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
kthelgason876222f2016-11-29 01:44:11 -08001220
asaperssonfab67072017-04-04 05:51:49 -07001221 new_video_source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001222 WaitForEncodedFrame(5);
kthelgason876222f2016-11-29 01:44:11 -08001223 stats = stats_proxy_->GetStats();
kthelgason876222f2016-11-29 01:44:11 -08001224 EXPECT_FALSE(stats.bw_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001225 EXPECT_FALSE(stats.bw_limited_framerate);
asaperssonfab67072017-04-04 05:51:49 -07001226 EXPECT_EQ(1, stats.number_of_quality_adapt_changes);
1227 EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
kthelgason876222f2016-11-29 01:44:11 -08001228
mflodmancc3d4422017-08-03 08:27:51 -07001229 video_stream_encoder_->Stop();
kthelgason876222f2016-11-29 01:44:11 -08001230}
1231
mflodmancc3d4422017-08-03 08:27:51 -07001232TEST_F(VideoStreamEncoderTest,
1233 QualityAdaptationStatsAreResetWhenScalerIsDisabled) {
1234 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson36e9eb42017-03-31 05:29:12 -07001235
1236 const int kWidth = 1280;
1237 const int kHeight = 720;
1238 video_source_.set_adaptation_enabled(true);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001239 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
1240 WaitForEncodedFrame(1);
asapersson36e9eb42017-03-31 05:29:12 -07001241 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1242 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1243 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1244
1245 // Trigger adapt down.
mflodmancc3d4422017-08-03 08:27:51 -07001246 video_stream_encoder_->TriggerQualityLow();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001247 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
1248 WaitForEncodedFrame(2);
asapersson36e9eb42017-03-31 05:29:12 -07001249 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1250 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1251 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1252
1253 // Trigger overuse.
mflodmancc3d4422017-08-03 08:27:51 -07001254 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001255 video_source_.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
1256 WaitForEncodedFrame(3);
asapersson36e9eb42017-03-31 05:29:12 -07001257 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1258 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1259 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1260
Niels Möller4db138e2018-04-19 09:04:13 +02001261 // Leave source unchanged, but disable quality scaler.
asapersson36e9eb42017-03-31 05:29:12 -07001262 fake_encoder_.SetQualityScaling(false);
Niels Möller4db138e2018-04-19 09:04:13 +02001263
1264 VideoEncoderConfig video_encoder_config;
1265 test::FillEncoderConfiguration(kVideoCodecVP8, 1, &video_encoder_config);
1266 // Make format different, to force recreation of encoder.
1267 video_encoder_config.video_format.parameters["foo"] = "foo";
1268 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02001269 kMaxPayloadLength);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001270
1271 video_source_.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
1272 WaitForEncodedFrame(4);
asapersson36e9eb42017-03-31 05:29:12 -07001273 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1274 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1275 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1276
mflodmancc3d4422017-08-03 08:27:51 -07001277 video_stream_encoder_->Stop();
asapersson36e9eb42017-03-31 05:29:12 -07001278}
1279
mflodmancc3d4422017-08-03 08:27:51 -07001280TEST_F(VideoStreamEncoderTest,
1281 StatsTracksCpuAdaptationStatsWhenSwitchingSource) {
1282 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
perkj803d97f2016-11-01 11:45:46 -07001283
asapersson0944a802017-04-07 00:57:58 -07001284 const int kWidth = 1280;
1285 const int kHeight = 720;
sprang84a37592017-02-10 07:04:27 -08001286 int sequence = 1;
perkj803d97f2016-11-01 11:45:46 -07001287
asaperssonfab67072017-04-04 05:51:49 -07001288 video_source_.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001289 WaitForEncodedFrame(sequence++);
perkj803d97f2016-11-01 11:45:46 -07001290 VideoSendStream::Stats stats = stats_proxy_->GetStats();
sprang84a37592017-02-10 07:04:27 -08001291 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001292 EXPECT_FALSE(stats.cpu_limited_framerate);
sprang84a37592017-02-10 07:04:27 -08001293 EXPECT_EQ(0, stats.number_of_cpu_adapt_changes);
1294
asapersson02465b82017-04-10 01:12:52 -07001295 // Trigger CPU overuse, should now adapt down.
mflodmancc3d4422017-08-03 08:27:51 -07001296 video_stream_encoder_->TriggerCpuOveruse();
asaperssonfab67072017-04-04 05:51:49 -07001297 video_source_.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001298 WaitForEncodedFrame(sequence++);
sprang84a37592017-02-10 07:04:27 -08001299 stats = stats_proxy_->GetStats();
perkj803d97f2016-11-01 11:45:46 -07001300 EXPECT_TRUE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001301 EXPECT_FALSE(stats.cpu_limited_framerate);
perkj803d97f2016-11-01 11:45:46 -07001302 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1303
1304 // Set new source with adaptation still enabled.
1305 test::FrameForwarder new_video_source;
mflodmancc3d4422017-08-03 08:27:51 -07001306 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001307 &new_video_source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
perkj803d97f2016-11-01 11:45:46 -07001308
1309 new_video_source.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07001310 CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001311 WaitForEncodedFrame(sequence++);
perkj803d97f2016-11-01 11:45:46 -07001312 stats = stats_proxy_->GetStats();
1313 EXPECT_TRUE(stats.cpu_limited_resolution);
asapersson09f05612017-05-15 23:40:18 -07001314 EXPECT_FALSE(stats.cpu_limited_framerate);
perkj803d97f2016-11-01 11:45:46 -07001315 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1316
sprangc5d62e22017-04-02 23:53:04 -07001317 // Set cpu adaptation by frame dropping.
mflodmancc3d4422017-08-03 08:27:51 -07001318 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001319 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
perkj803d97f2016-11-01 11:45:46 -07001320 new_video_source.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07001321 CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001322 WaitForEncodedFrame(sequence++);
perkj803d97f2016-11-01 11:45:46 -07001323 stats = stats_proxy_->GetStats();
sprangc5d62e22017-04-02 23:53:04 -07001324 // Not adapted at first.
perkj803d97f2016-11-01 11:45:46 -07001325 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson09f05612017-05-15 23:40:18 -07001326 EXPECT_FALSE(stats.cpu_limited_framerate);
perkj803d97f2016-11-01 11:45:46 -07001327 EXPECT_EQ(1, stats.number_of_cpu_adapt_changes);
1328
sprangc5d62e22017-04-02 23:53:04 -07001329 // Force an input frame rate to be available, or the adaptation call won't
asapersson09f05612017-05-15 23:40:18 -07001330 // know what framerate to adapt from.
sprangc5d62e22017-04-02 23:53:04 -07001331 VideoSendStream::Stats mock_stats = stats_proxy_->GetStats();
1332 mock_stats.input_frame_rate = 30;
1333 stats_proxy_->SetMockStats(mock_stats);
mflodmancc3d4422017-08-03 08:27:51 -07001334 video_stream_encoder_->TriggerCpuOveruse();
sprangc5d62e22017-04-02 23:53:04 -07001335 stats_proxy_->ResetMockStats();
1336
1337 new_video_source.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07001338 CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001339 WaitForEncodedFrame(sequence++);
sprangc5d62e22017-04-02 23:53:04 -07001340
1341 // Framerate now adapted.
1342 stats = stats_proxy_->GetStats();
asapersson09f05612017-05-15 23:40:18 -07001343 EXPECT_FALSE(stats.cpu_limited_resolution);
1344 EXPECT_TRUE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001345 EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
1346
1347 // Disable CPU adaptation.
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001348 video_stream_encoder_->SetSource(&new_video_source,
1349 webrtc::DegradationPreference::DISABLED);
sprangc5d62e22017-04-02 23:53:04 -07001350 new_video_source.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07001351 CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001352 WaitForEncodedFrame(sequence++);
sprangc5d62e22017-04-02 23:53:04 -07001353
1354 stats = stats_proxy_->GetStats();
1355 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001356 EXPECT_FALSE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001357 EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
1358
1359 // Try to trigger overuse. Should not succeed.
1360 stats_proxy_->SetMockStats(mock_stats);
mflodmancc3d4422017-08-03 08:27:51 -07001361 video_stream_encoder_->TriggerCpuOveruse();
sprangc5d62e22017-04-02 23:53:04 -07001362 stats_proxy_->ResetMockStats();
1363
1364 stats = stats_proxy_->GetStats();
1365 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001366 EXPECT_FALSE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001367 EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
1368
1369 // Switch back the source with resolution adaptation enabled.
mflodmancc3d4422017-08-03 08:27:51 -07001370 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001371 &video_source_, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asaperssonfab67072017-04-04 05:51:49 -07001372 video_source_.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001373 WaitForEncodedFrame(sequence++);
perkj803d97f2016-11-01 11:45:46 -07001374 stats = stats_proxy_->GetStats();
1375 EXPECT_TRUE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001376 EXPECT_FALSE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001377 EXPECT_EQ(2, stats.number_of_cpu_adapt_changes);
perkj803d97f2016-11-01 11:45:46 -07001378
1379 // Trigger CPU normal usage.
mflodmancc3d4422017-08-03 08:27:51 -07001380 video_stream_encoder_->TriggerCpuNormalUsage();
asaperssonfab67072017-04-04 05:51:49 -07001381 video_source_.IncomingCapturedFrame(CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001382 WaitForEncodedFrame(sequence++);
perkj803d97f2016-11-01 11:45:46 -07001383 stats = stats_proxy_->GetStats();
1384 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001385 EXPECT_FALSE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001386 EXPECT_EQ(3, stats.number_of_cpu_adapt_changes);
1387
1388 // Back to the source with adaptation off, set it back to maintain-resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001389 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001390 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
sprangc5d62e22017-04-02 23:53:04 -07001391 new_video_source.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07001392 CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001393 WaitForEncodedFrame(sequence++);
sprangc5d62e22017-04-02 23:53:04 -07001394 stats = stats_proxy_->GetStats();
asapersson13874762017-06-07 00:01:02 -07001395 // Disabled, since we previously switched the source to disabled.
sprangc5d62e22017-04-02 23:53:04 -07001396 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001397 EXPECT_TRUE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001398 EXPECT_EQ(3, stats.number_of_cpu_adapt_changes);
1399
1400 // Trigger CPU normal usage.
mflodmancc3d4422017-08-03 08:27:51 -07001401 video_stream_encoder_->TriggerCpuNormalUsage();
sprangc5d62e22017-04-02 23:53:04 -07001402 new_video_source.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07001403 CreateFrame(sequence, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001404 WaitForEncodedFrame(sequence++);
sprangc5d62e22017-04-02 23:53:04 -07001405 stats = stats_proxy_->GetStats();
1406 EXPECT_FALSE(stats.cpu_limited_resolution);
asapersson13874762017-06-07 00:01:02 -07001407 EXPECT_FALSE(stats.cpu_limited_framerate);
sprangc5d62e22017-04-02 23:53:04 -07001408 EXPECT_EQ(4, stats.number_of_cpu_adapt_changes);
asapersson02465b82017-04-10 01:12:52 -07001409 EXPECT_EQ(0, stats.number_of_quality_adapt_changes);
perkj803d97f2016-11-01 11:45:46 -07001410
mflodmancc3d4422017-08-03 08:27:51 -07001411 video_stream_encoder_->Stop();
perkj803d97f2016-11-01 11:45:46 -07001412}
1413
mflodmancc3d4422017-08-03 08:27:51 -07001414TEST_F(VideoStreamEncoderTest,
1415 ScalingUpAndDownDoesNothingWithMaintainResolution) {
asaperssonfab67072017-04-04 05:51:49 -07001416 const int kWidth = 1280;
1417 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001418 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
kthelgason876222f2016-11-29 01:44:11 -08001419
asaperssonfab67072017-04-04 05:51:49 -07001420 // Expect no scaling to begin with.
asapersson02465b82017-04-10 01:12:52 -07001421 VerifyNoLimitation(video_source_.sink_wants());
kthelgason876222f2016-11-29 01:44:11 -08001422
asaperssonfab67072017-04-04 05:51:49 -07001423 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001424 WaitForEncodedFrame(1);
kthelgason876222f2016-11-29 01:44:11 -08001425
asaperssonfab67072017-04-04 05:51:49 -07001426 // Trigger scale down.
mflodmancc3d4422017-08-03 08:27:51 -07001427 video_stream_encoder_->TriggerQualityLow();
kthelgason5e13d412016-12-01 03:59:51 -08001428
asaperssonfab67072017-04-04 05:51:49 -07001429 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001430 WaitForEncodedFrame(2);
kthelgason5e13d412016-12-01 03:59:51 -08001431
kthelgason876222f2016-11-29 01:44:11 -08001432 // Expect a scale down.
1433 EXPECT_TRUE(video_source_.sink_wants().max_pixel_count);
asaperssonfab67072017-04-04 05:51:49 -07001434 EXPECT_LT(video_source_.sink_wants().max_pixel_count, kWidth * kHeight);
kthelgason876222f2016-11-29 01:44:11 -08001435
asapersson02465b82017-04-10 01:12:52 -07001436 // Set resolution scaling disabled.
kthelgason876222f2016-11-29 01:44:11 -08001437 test::FrameForwarder new_video_source;
mflodmancc3d4422017-08-03 08:27:51 -07001438 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001439 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
kthelgason876222f2016-11-29 01:44:11 -08001440
asaperssonfab67072017-04-04 05:51:49 -07001441 // Trigger scale down.
mflodmancc3d4422017-08-03 08:27:51 -07001442 video_stream_encoder_->TriggerQualityLow();
asaperssonfab67072017-04-04 05:51:49 -07001443 new_video_source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001444 WaitForEncodedFrame(3);
kthelgason876222f2016-11-29 01:44:11 -08001445
asaperssonfab67072017-04-04 05:51:49 -07001446 // Expect no scaling.
sprangc5d62e22017-04-02 23:53:04 -07001447 EXPECT_EQ(std::numeric_limits<int>::max(),
1448 new_video_source.sink_wants().max_pixel_count);
kthelgason876222f2016-11-29 01:44:11 -08001449
asaperssonfab67072017-04-04 05:51:49 -07001450 // Trigger scale up.
mflodmancc3d4422017-08-03 08:27:51 -07001451 video_stream_encoder_->TriggerQualityHigh();
asaperssonfab67072017-04-04 05:51:49 -07001452 new_video_source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001453 WaitForEncodedFrame(4);
kthelgason876222f2016-11-29 01:44:11 -08001454
asapersson02465b82017-04-10 01:12:52 -07001455 // Expect nothing to change, still no scaling.
sprangc5d62e22017-04-02 23:53:04 -07001456 EXPECT_EQ(std::numeric_limits<int>::max(),
1457 new_video_source.sink_wants().max_pixel_count);
kthelgason876222f2016-11-29 01:44:11 -08001458
mflodmancc3d4422017-08-03 08:27:51 -07001459 video_stream_encoder_->Stop();
kthelgason876222f2016-11-29 01:44:11 -08001460}
1461
mflodmancc3d4422017-08-03 08:27:51 -07001462TEST_F(VideoStreamEncoderTest,
1463 SkipsSameAdaptDownRequest_MaintainFramerateMode) {
asapersson02465b82017-04-10 01:12:52 -07001464 const int kWidth = 1280;
1465 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001466 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson02465b82017-04-10 01:12:52 -07001467
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001468 // Enable MAINTAIN_FRAMERATE preference, no initial limitation.
asapersson02465b82017-04-10 01:12:52 -07001469 test::FrameForwarder source;
mflodmancc3d4422017-08-03 08:27:51 -07001470 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001471 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asapersson02465b82017-04-10 01:12:52 -07001472
1473 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001474 WaitForEncodedFrame(1);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001475 VerifyNoLimitation(source.sink_wants());
asapersson02465b82017-04-10 01:12:52 -07001476 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1477 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1478
1479 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001480 video_stream_encoder_->TriggerCpuOveruse();
asapersson09f05612017-05-15 23:40:18 -07001481 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
asapersson02465b82017-04-10 01:12:52 -07001482 const int kLastMaxPixelCount = source.sink_wants().max_pixel_count;
1483 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1484 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1485
1486 // Trigger adapt down for same input resolution, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07001487 video_stream_encoder_->TriggerCpuOveruse();
asapersson02465b82017-04-10 01:12:52 -07001488 EXPECT_EQ(kLastMaxPixelCount, source.sink_wants().max_pixel_count);
1489 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1490 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1491
mflodmancc3d4422017-08-03 08:27:51 -07001492 video_stream_encoder_->Stop();
asapersson02465b82017-04-10 01:12:52 -07001493}
1494
mflodmancc3d4422017-08-03 08:27:51 -07001495TEST_F(VideoStreamEncoderTest, SkipsSameOrLargerAdaptDownRequest_BalancedMode) {
asaperssonf7e294d2017-06-13 23:25:22 -07001496 const int kWidth = 1280;
1497 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001498 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07001499
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001500 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07001501 test::FrameForwarder source;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001502 video_stream_encoder_->SetSource(&source,
1503 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07001504 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
1505 sink_.WaitForEncodedFrame(1);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001506 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07001507
1508 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001509 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07001510 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
1511 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1512 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1513 const int kLastMaxPixelCount = source.sink_wants().max_pixel_count;
1514
1515 // Trigger adapt down for same input resolution, expect no change.
1516 source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
1517 sink_.WaitForEncodedFrame(2);
mflodmancc3d4422017-08-03 08:27:51 -07001518 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07001519 EXPECT_EQ(kLastMaxPixelCount, source.sink_wants().max_pixel_count);
1520 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1521 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1522
1523 // Trigger adapt down for larger input resolution, expect no change.
1524 source.IncomingCapturedFrame(CreateFrame(3, kWidth + 1, kHeight + 1));
1525 sink_.WaitForEncodedFrame(3);
mflodmancc3d4422017-08-03 08:27:51 -07001526 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07001527 EXPECT_EQ(kLastMaxPixelCount, source.sink_wants().max_pixel_count);
1528 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1529 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1530
mflodmancc3d4422017-08-03 08:27:51 -07001531 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07001532}
1533
mflodmancc3d4422017-08-03 08:27:51 -07001534TEST_F(VideoStreamEncoderTest,
1535 NoChangeForInitialNormalUsage_MaintainFramerateMode) {
asapersson02465b82017-04-10 01:12:52 -07001536 const int kWidth = 1280;
1537 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001538 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson02465b82017-04-10 01:12:52 -07001539
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001540 // Enable MAINTAIN_FRAMERATE preference, no initial limitation.
asapersson02465b82017-04-10 01:12:52 -07001541 test::FrameForwarder source;
mflodmancc3d4422017-08-03 08:27:51 -07001542 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001543 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asapersson02465b82017-04-10 01:12:52 -07001544
1545 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001546 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001547 VerifyNoLimitation(source.sink_wants());
asapersson02465b82017-04-10 01:12:52 -07001548 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1549 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1550
1551 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07001552 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001553 VerifyNoLimitation(source.sink_wants());
asapersson02465b82017-04-10 01:12:52 -07001554 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1555 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1556
mflodmancc3d4422017-08-03 08:27:51 -07001557 video_stream_encoder_->Stop();
asapersson02465b82017-04-10 01:12:52 -07001558}
1559
mflodmancc3d4422017-08-03 08:27:51 -07001560TEST_F(VideoStreamEncoderTest,
1561 NoChangeForInitialNormalUsage_MaintainResolutionMode) {
asapersson02465b82017-04-10 01:12:52 -07001562 const int kWidth = 1280;
1563 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001564 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson02465b82017-04-10 01:12:52 -07001565
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001566 // Enable MAINTAIN_RESOLUTION preference, no initial limitation.
asapersson02465b82017-04-10 01:12:52 -07001567 test::FrameForwarder source;
mflodmancc3d4422017-08-03 08:27:51 -07001568 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001569 &source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
asapersson02465b82017-04-10 01:12:52 -07001570
1571 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001572 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001573 VerifyNoLimitation(source.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001574 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
asapersson02465b82017-04-10 01:12:52 -07001575 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1576
1577 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07001578 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001579 VerifyNoLimitation(source.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001580 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
asapersson02465b82017-04-10 01:12:52 -07001581 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1582
mflodmancc3d4422017-08-03 08:27:51 -07001583 video_stream_encoder_->Stop();
asapersson02465b82017-04-10 01:12:52 -07001584}
1585
mflodmancc3d4422017-08-03 08:27:51 -07001586TEST_F(VideoStreamEncoderTest, NoChangeForInitialNormalUsage_BalancedMode) {
asaperssonf7e294d2017-06-13 23:25:22 -07001587 const int kWidth = 1280;
1588 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001589 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07001590
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001591 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07001592 test::FrameForwarder source;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001593 video_stream_encoder_->SetSource(&source,
1594 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07001595
1596 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
1597 sink_.WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001598 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07001599 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1600 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
1601 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1602
1603 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07001604 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001605 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07001606 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1607 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
1608 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1609
mflodmancc3d4422017-08-03 08:27:51 -07001610 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07001611}
1612
mflodmancc3d4422017-08-03 08:27:51 -07001613TEST_F(VideoStreamEncoderTest, NoChangeForInitialNormalUsage_DisabledMode) {
asapersson09f05612017-05-15 23:40:18 -07001614 const int kWidth = 1280;
1615 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001616 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson09f05612017-05-15 23:40:18 -07001617
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001618 // Enable DISABLED preference, no initial limitation.
asapersson09f05612017-05-15 23:40:18 -07001619 test::FrameForwarder source;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001620 video_stream_encoder_->SetSource(&source,
1621 webrtc::DegradationPreference::DISABLED);
asapersson09f05612017-05-15 23:40:18 -07001622
1623 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
1624 sink_.WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001625 VerifyNoLimitation(source.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001626 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1627 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
1628 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1629
1630 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07001631 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001632 VerifyNoLimitation(source.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001633 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1634 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
1635 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1636
mflodmancc3d4422017-08-03 08:27:51 -07001637 video_stream_encoder_->Stop();
asapersson09f05612017-05-15 23:40:18 -07001638}
1639
mflodmancc3d4422017-08-03 08:27:51 -07001640TEST_F(VideoStreamEncoderTest,
1641 AdaptsResolutionForLowQuality_MaintainFramerateMode) {
asapersson02465b82017-04-10 01:12:52 -07001642 const int kWidth = 1280;
1643 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001644 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson02465b82017-04-10 01:12:52 -07001645
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001646 // Enable MAINTAIN_FRAMERATE preference, no initial limitation.
asapersson02465b82017-04-10 01:12:52 -07001647 AdaptingFrameForwarder source;
1648 source.set_adaptation_enabled(true);
mflodmancc3d4422017-08-03 08:27:51 -07001649 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001650 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asapersson02465b82017-04-10 01:12:52 -07001651
1652 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001653 WaitForEncodedFrame(1);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001654 VerifyNoLimitation(source.sink_wants());
asapersson02465b82017-04-10 01:12:52 -07001655 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1656 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1657
1658 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001659 video_stream_encoder_->TriggerQualityLow();
asapersson02465b82017-04-10 01:12:52 -07001660 source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001661 WaitForEncodedFrame(2);
asapersson09f05612017-05-15 23:40:18 -07001662 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
asapersson02465b82017-04-10 01:12:52 -07001663 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1664 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1665
1666 // Trigger adapt up, expect no restriction.
mflodmancc3d4422017-08-03 08:27:51 -07001667 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001668 VerifyNoLimitation(source.sink_wants());
asapersson02465b82017-04-10 01:12:52 -07001669 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1670 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1671 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1672
mflodmancc3d4422017-08-03 08:27:51 -07001673 video_stream_encoder_->Stop();
asapersson02465b82017-04-10 01:12:52 -07001674}
1675
mflodmancc3d4422017-08-03 08:27:51 -07001676TEST_F(VideoStreamEncoderTest,
1677 AdaptsFramerateForLowQuality_MaintainResolutionMode) {
asapersson09f05612017-05-15 23:40:18 -07001678 const int kWidth = 1280;
1679 const int kHeight = 720;
1680 const int kInputFps = 30;
mflodmancc3d4422017-08-03 08:27:51 -07001681 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asapersson09f05612017-05-15 23:40:18 -07001682
1683 VideoSendStream::Stats stats = stats_proxy_->GetStats();
1684 stats.input_frame_rate = kInputFps;
1685 stats_proxy_->SetMockStats(stats);
1686
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001687 // Expect no scaling to begin with (preference: MAINTAIN_FRAMERATE).
asapersson09f05612017-05-15 23:40:18 -07001688 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
1689 sink_.WaitForEncodedFrame(1);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001690 VerifyNoLimitation(video_source_.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001691
1692 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001693 video_stream_encoder_->TriggerQualityLow();
asapersson09f05612017-05-15 23:40:18 -07001694 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
1695 sink_.WaitForEncodedFrame(2);
1696 VerifyFpsMaxResolutionLt(video_source_.sink_wants(), kWidth * kHeight);
1697
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001698 // Enable MAINTAIN_RESOLUTION preference.
asapersson09f05612017-05-15 23:40:18 -07001699 test::FrameForwarder new_video_source;
mflodmancc3d4422017-08-03 08:27:51 -07001700 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001701 &new_video_source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001702 VerifyNoLimitation(new_video_source.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001703
1704 // Trigger adapt down, expect reduced framerate.
mflodmancc3d4422017-08-03 08:27:51 -07001705 video_stream_encoder_->TriggerQualityLow();
asapersson09f05612017-05-15 23:40:18 -07001706 new_video_source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
1707 sink_.WaitForEncodedFrame(3);
1708 VerifyFpsLtResolutionMax(new_video_source.sink_wants(), kInputFps);
1709
1710 // Trigger adapt up, expect no restriction.
mflodmancc3d4422017-08-03 08:27:51 -07001711 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001712 VerifyNoLimitation(new_video_source.sink_wants());
asapersson09f05612017-05-15 23:40:18 -07001713
mflodmancc3d4422017-08-03 08:27:51 -07001714 video_stream_encoder_->Stop();
asapersson09f05612017-05-15 23:40:18 -07001715}
1716
mflodmancc3d4422017-08-03 08:27:51 -07001717TEST_F(VideoStreamEncoderTest, DoesNotScaleBelowSetResolutionLimit) {
asaperssond0de2952017-04-21 01:47:31 -07001718 const int kWidth = 1280;
1719 const int kHeight = 720;
1720 const size_t kNumFrames = 10;
1721
mflodmancc3d4422017-08-03 08:27:51 -07001722 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
kthelgason5e13d412016-12-01 03:59:51 -08001723
asaperssond0de2952017-04-21 01:47:31 -07001724 // Enable adapter, expected input resolutions when downscaling:
asapersson142fcc92017-08-17 08:58:54 -07001725 // 1280x720 -> 960x540 -> 640x360 -> 480x270 -> 320x180 (kMinPixelsPerFrame)
asaperssond0de2952017-04-21 01:47:31 -07001726 video_source_.set_adaptation_enabled(true);
1727
1728 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1729 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1730
1731 int downscales = 0;
1732 for (size_t i = 1; i <= kNumFrames; i++) {
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001733 video_source_.IncomingCapturedFrame(CreateFrame(i, kWidth, kHeight));
1734 WaitForEncodedFrame(i);
asaperssond0de2952017-04-21 01:47:31 -07001735
asaperssonfab67072017-04-04 05:51:49 -07001736 // Trigger scale down.
asaperssond0de2952017-04-21 01:47:31 -07001737 rtc::VideoSinkWants last_wants = video_source_.sink_wants();
mflodmancc3d4422017-08-03 08:27:51 -07001738 video_stream_encoder_->TriggerQualityLow();
sprangc5d62e22017-04-02 23:53:04 -07001739 EXPECT_GE(video_source_.sink_wants().max_pixel_count, kMinPixelsPerFrame);
asaperssond0de2952017-04-21 01:47:31 -07001740
1741 if (video_source_.sink_wants().max_pixel_count < last_wants.max_pixel_count)
1742 ++downscales;
1743
1744 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1745 EXPECT_EQ(downscales,
1746 stats_proxy_->GetStats().number_of_quality_adapt_changes);
1747 EXPECT_GT(downscales, 0);
kthelgason5e13d412016-12-01 03:59:51 -08001748 }
mflodmancc3d4422017-08-03 08:27:51 -07001749 video_stream_encoder_->Stop();
asaperssond0de2952017-04-21 01:47:31 -07001750}
1751
mflodmancc3d4422017-08-03 08:27:51 -07001752TEST_F(VideoStreamEncoderTest,
asaperssond0de2952017-04-21 01:47:31 -07001753 AdaptsResolutionUpAndDownTwiceOnOveruse_MaintainFramerateMode) {
1754 const int kWidth = 1280;
1755 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001756 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssond0de2952017-04-21 01:47:31 -07001757
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001758 // Enable MAINTAIN_FRAMERATE preference, no initial limitation.
asaperssond0de2952017-04-21 01:47:31 -07001759 AdaptingFrameForwarder source;
1760 source.set_adaptation_enabled(true);
mflodmancc3d4422017-08-03 08:27:51 -07001761 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001762 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asaperssond0de2952017-04-21 01:47:31 -07001763
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001764 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001765 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001766 VerifyNoLimitation(source.sink_wants());
asaperssond0de2952017-04-21 01:47:31 -07001767 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1768 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1769
1770 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001771 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001772 source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
1773 WaitForEncodedFrame(2);
asapersson09f05612017-05-15 23:40:18 -07001774 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
asaperssond0de2952017-04-21 01:47:31 -07001775 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1776 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1777
1778 // Trigger adapt up, expect no restriction.
mflodmancc3d4422017-08-03 08:27:51 -07001779 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001780 source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001781 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001782 VerifyNoLimitation(source.sink_wants());
asaperssond0de2952017-04-21 01:47:31 -07001783 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1784 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1785
1786 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001787 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001788 source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
1789 WaitForEncodedFrame(4);
asapersson09f05612017-05-15 23:40:18 -07001790 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
asaperssond0de2952017-04-21 01:47:31 -07001791 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1792 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1793
1794 // Trigger adapt up, expect no restriction.
mflodmancc3d4422017-08-03 08:27:51 -07001795 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001796 source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
asapersson09f05612017-05-15 23:40:18 -07001797 sink_.WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001798 VerifyNoLimitation(source.sink_wants());
asaperssond0de2952017-04-21 01:47:31 -07001799 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1800 EXPECT_EQ(4, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1801
mflodmancc3d4422017-08-03 08:27:51 -07001802 video_stream_encoder_->Stop();
asaperssond0de2952017-04-21 01:47:31 -07001803}
1804
mflodmancc3d4422017-08-03 08:27:51 -07001805TEST_F(VideoStreamEncoderTest,
asaperssonf7e294d2017-06-13 23:25:22 -07001806 AdaptsResolutionUpAndDownTwiceForLowQuality_BalancedMode_NoFpsLimit) {
1807 const int kWidth = 1280;
1808 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001809 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07001810
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001811 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07001812 AdaptingFrameForwarder source;
1813 source.set_adaptation_enabled(true);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001814 video_stream_encoder_->SetSource(&source,
1815 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07001816
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001817 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
asaperssonf7e294d2017-06-13 23:25:22 -07001818 sink_.WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001819 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07001820 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1821 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1822
1823 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001824 video_stream_encoder_->TriggerQualityLow();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001825 source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
1826 sink_.WaitForEncodedFrame(2);
asaperssonf7e294d2017-06-13 23:25:22 -07001827 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
1828 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1829 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1830
1831 // Trigger adapt up, expect no restriction.
mflodmancc3d4422017-08-03 08:27:51 -07001832 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001833 source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
asaperssonf7e294d2017-06-13 23:25:22 -07001834 sink_.WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001835 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07001836 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1837 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1838
1839 // Trigger adapt down, expect scaled down resolution.
mflodmancc3d4422017-08-03 08:27:51 -07001840 video_stream_encoder_->TriggerQualityLow();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001841 source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
1842 sink_.WaitForEncodedFrame(4);
asaperssonf7e294d2017-06-13 23:25:22 -07001843 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
1844 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1845 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1846
1847 // Trigger adapt up, expect no restriction.
mflodmancc3d4422017-08-03 08:27:51 -07001848 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001849 source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
asaperssonf7e294d2017-06-13 23:25:22 -07001850 sink_.WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001851 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07001852 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1853 EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1854
mflodmancc3d4422017-08-03 08:27:51 -07001855 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07001856}
1857
mflodmancc3d4422017-08-03 08:27:51 -07001858TEST_F(VideoStreamEncoderTest,
asaperssond0de2952017-04-21 01:47:31 -07001859 AdaptsResolutionOnOveruseAndLowQuality_MaintainFramerateMode) {
1860 const int kWidth = 1280;
1861 const int kHeight = 720;
mflodmancc3d4422017-08-03 08:27:51 -07001862 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssond0de2952017-04-21 01:47:31 -07001863
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001864 // Enable MAINTAIN_FRAMERATE preference, no initial limitation.
asaperssond0de2952017-04-21 01:47:31 -07001865 AdaptingFrameForwarder source;
1866 source.set_adaptation_enabled(true);
mflodmancc3d4422017-08-03 08:27:51 -07001867 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07001868 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asaperssond0de2952017-04-21 01:47:31 -07001869
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001870 source.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001871 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001872 VerifyNoLimitation(source.sink_wants());
asaperssond0de2952017-04-21 01:47:31 -07001873 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1874 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1875 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1876 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1877
1878 // Trigger cpu adapt down, expect scaled down resolution (960x540).
mflodmancc3d4422017-08-03 08:27:51 -07001879 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001880 source.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
1881 WaitForEncodedFrame(2);
asapersson09f05612017-05-15 23:40:18 -07001882 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
asaperssond0de2952017-04-21 01:47:31 -07001883 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1884 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1885 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1886 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1887
1888 // Trigger cpu adapt down, expect scaled down resolution (640x360).
mflodmancc3d4422017-08-03 08:27:51 -07001889 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001890 source.IncomingCapturedFrame(CreateFrame(3, kWidth, kHeight));
1891 WaitForEncodedFrame(3);
asapersson09f05612017-05-15 23:40:18 -07001892 VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
asaperssond0de2952017-04-21 01:47:31 -07001893 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1894 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
1895 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1896 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1897
Jonathan Yubc771b72017-12-08 17:04:29 -08001898 // Trigger cpu adapt down, expect scaled down resolution (480x270).
mflodmancc3d4422017-08-03 08:27:51 -07001899 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001900 source.IncomingCapturedFrame(CreateFrame(4, kWidth, kHeight));
1901 WaitForEncodedFrame(4);
Jonathan Yubc771b72017-12-08 17:04:29 -08001902 VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
asaperssond0de2952017-04-21 01:47:31 -07001903 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1904 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -08001905 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
asaperssond0de2952017-04-21 01:47:31 -07001906 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1907
Jonathan Yubc771b72017-12-08 17:04:29 -08001908 // Trigger quality adapt down, expect scaled down resolution (320x180).
mflodmancc3d4422017-08-03 08:27:51 -07001909 video_stream_encoder_->TriggerQualityLow();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001910 source.IncomingCapturedFrame(CreateFrame(5, kWidth, kHeight));
1911 WaitForEncodedFrame(5);
asapersson09f05612017-05-15 23:40:18 -07001912 VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
Jonathan Yubc771b72017-12-08 17:04:29 -08001913 rtc::VideoSinkWants last_wants = source.sink_wants();
asaperssond0de2952017-04-21 01:47:31 -07001914 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1915 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1916 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1917 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1918
Jonathan Yubc771b72017-12-08 17:04:29 -08001919 // Trigger quality adapt down, expect no change (min resolution reached).
1920 video_stream_encoder_->TriggerQualityLow();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001921 source.IncomingCapturedFrame(CreateFrame(6, kWidth, kHeight));
1922 WaitForEncodedFrame(6);
Jonathan Yubc771b72017-12-08 17:04:29 -08001923 VerifyFpsMaxResolutionEq(source.sink_wants(), last_wants);
1924 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1925 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1926 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1927 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1928
1929 // Trigger cpu adapt up, expect upscaled resolution (480x270).
mflodmancc3d4422017-08-03 08:27:51 -07001930 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001931 source.IncomingCapturedFrame(CreateFrame(7, kWidth, kHeight));
1932 WaitForEncodedFrame(7);
asapersson09f05612017-05-15 23:40:18 -07001933 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
Jonathan Yubc771b72017-12-08 17:04:29 -08001934 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1935 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1936 EXPECT_EQ(4, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1937 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1938
1939 // Trigger cpu adapt up, expect upscaled resolution (640x360).
1940 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001941 source.IncomingCapturedFrame(CreateFrame(8, kWidth, kHeight));
1942 WaitForEncodedFrame(8);
Jonathan Yubc771b72017-12-08 17:04:29 -08001943 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
1944 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
1945 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
1946 EXPECT_EQ(5, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
1947 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1948
1949 // Trigger cpu adapt up, expect upscaled resolution (960x540).
1950 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001951 source.IncomingCapturedFrame(CreateFrame(9, kWidth, kHeight));
1952 WaitForEncodedFrame(9);
Jonathan Yubc771b72017-12-08 17:04:29 -08001953 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
asaperssond0de2952017-04-21 01:47:31 -07001954 last_wants = source.sink_wants();
1955 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1956 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -08001957 EXPECT_EQ(6, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
asaperssond0de2952017-04-21 01:47:31 -07001958 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1959
1960 // Trigger cpu adapt up, no cpu downgrades, expect no change (960x540).
mflodmancc3d4422017-08-03 08:27:51 -07001961 video_stream_encoder_->TriggerCpuNormalUsage();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001962 source.IncomingCapturedFrame(CreateFrame(10, kWidth, kHeight));
1963 WaitForEncodedFrame(10);
asapersson09f05612017-05-15 23:40:18 -07001964 VerifyFpsEqResolutionEq(source.sink_wants(), last_wants);
asaperssond0de2952017-04-21 01:47:31 -07001965 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1966 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -08001967 EXPECT_EQ(6, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
asaperssond0de2952017-04-21 01:47:31 -07001968 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
1969
1970 // Trigger quality adapt up, expect no restriction (1280x720).
mflodmancc3d4422017-08-03 08:27:51 -07001971 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001972 source.IncomingCapturedFrame(CreateFrame(11, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001973 WaitForEncodedFrame(kWidth, kHeight);
asapersson09f05612017-05-15 23:40:18 -07001974 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
Mirko Bonadei948b7e32018-08-14 07:23:21 +00001975 VerifyNoLimitation(source.sink_wants());
asaperssond0de2952017-04-21 01:47:31 -07001976 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
1977 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
Jonathan Yubc771b72017-12-08 17:04:29 -08001978 EXPECT_EQ(6, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
asaperssond0de2952017-04-21 01:47:31 -07001979 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
kthelgason5e13d412016-12-01 03:59:51 -08001980
mflodmancc3d4422017-08-03 08:27:51 -07001981 video_stream_encoder_->Stop();
kthelgason5e13d412016-12-01 03:59:51 -08001982}
1983
mflodmancc3d4422017-08-03 08:27:51 -07001984TEST_F(VideoStreamEncoderTest, CpuLimitedHistogramIsReported) {
asaperssonfab67072017-04-04 05:51:49 -07001985 const int kWidth = 640;
1986 const int kHeight = 360;
perkj803d97f2016-11-01 11:45:46 -07001987
mflodmancc3d4422017-08-03 08:27:51 -07001988 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
sprang4847ae62017-06-27 07:06:52 -07001989
perkj803d97f2016-11-01 11:45:46 -07001990 for (int i = 1; i <= SendStatisticsProxy::kMinRequiredMetricsSamples; ++i) {
asaperssonfab67072017-04-04 05:51:49 -07001991 video_source_.IncomingCapturedFrame(CreateFrame(i, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001992 WaitForEncodedFrame(i);
perkj803d97f2016-11-01 11:45:46 -07001993 }
1994
mflodmancc3d4422017-08-03 08:27:51 -07001995 video_stream_encoder_->TriggerCpuOveruse();
perkj803d97f2016-11-01 11:45:46 -07001996 for (int i = 1; i <= SendStatisticsProxy::kMinRequiredMetricsSamples; ++i) {
asaperssonfab67072017-04-04 05:51:49 -07001997 video_source_.IncomingCapturedFrame(CreateFrame(
1998 SendStatisticsProxy::kMinRequiredMetricsSamples + i, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07001999 WaitForEncodedFrame(SendStatisticsProxy::kMinRequiredMetricsSamples + i);
perkj803d97f2016-11-01 11:45:46 -07002000 }
2001
mflodmancc3d4422017-08-03 08:27:51 -07002002 video_stream_encoder_->Stop();
2003 video_stream_encoder_.reset();
perkj803d97f2016-11-01 11:45:46 -07002004 stats_proxy_.reset();
sprangf8ee65e2017-02-28 08:49:33 -08002005
perkj803d97f2016-11-01 11:45:46 -07002006 EXPECT_EQ(1,
2007 metrics::NumSamples("WebRTC.Video.CpuLimitedResolutionInPercent"));
2008 EXPECT_EQ(
2009 1, metrics::NumEvents("WebRTC.Video.CpuLimitedResolutionInPercent", 50));
2010}
2011
mflodmancc3d4422017-08-03 08:27:51 -07002012TEST_F(VideoStreamEncoderTest,
2013 CpuLimitedHistogramIsNotReportedForDisabledDegradation) {
2014 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf4e44af2017-04-19 02:01:06 -07002015 const int kWidth = 640;
2016 const int kHeight = 360;
2017
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002018 video_stream_encoder_->SetSource(&video_source_,
2019 webrtc::DegradationPreference::DISABLED);
asaperssonf4e44af2017-04-19 02:01:06 -07002020
2021 for (int i = 1; i <= SendStatisticsProxy::kMinRequiredMetricsSamples; ++i) {
2022 video_source_.IncomingCapturedFrame(CreateFrame(i, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002023 WaitForEncodedFrame(i);
asaperssonf4e44af2017-04-19 02:01:06 -07002024 }
2025
mflodmancc3d4422017-08-03 08:27:51 -07002026 video_stream_encoder_->Stop();
2027 video_stream_encoder_.reset();
asaperssonf4e44af2017-04-19 02:01:06 -07002028 stats_proxy_.reset();
2029
2030 EXPECT_EQ(0,
2031 metrics::NumSamples("WebRTC.Video.CpuLimitedResolutionInPercent"));
2032}
2033
mflodmancc3d4422017-08-03 08:27:51 -07002034TEST_F(VideoStreamEncoderTest, CallsBitrateObserver) {
sprang4847ae62017-06-27 07:06:52 -07002035 MockBitrateObserver bitrate_observer;
Niels Möller0327c2d2018-05-21 14:09:31 +02002036 video_stream_encoder_->SetBitrateAllocationObserver(&bitrate_observer);
sprang57c2fff2017-01-16 06:24:02 -08002037
2038 const int kDefaultFps = 30;
Erik Språng566124a2018-04-23 12:32:22 +02002039 const VideoBitrateAllocation expected_bitrate =
sprang57c2fff2017-01-16 06:24:02 -08002040 DefaultVideoBitrateAllocator(fake_encoder_.codec_config())
kthelgason2bc68642017-02-07 07:02:22 -08002041 .GetAllocation(kLowTargetBitrateBps, kDefaultFps);
sprang57c2fff2017-01-16 06:24:02 -08002042
2043 // First called on bitrate updated, then again on first frame.
2044 EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(expected_bitrate))
2045 .Times(2);
mflodmancc3d4422017-08-03 08:27:51 -07002046 video_stream_encoder_->OnBitrateUpdated(kLowTargetBitrateBps, 0, 0);
sprang57c2fff2017-01-16 06:24:02 -08002047
2048 const int64_t kStartTimeMs = 1;
2049 video_source_.IncomingCapturedFrame(
2050 CreateFrame(kStartTimeMs, codec_width_, codec_height_));
sprang4847ae62017-06-27 07:06:52 -07002051 WaitForEncodedFrame(kStartTimeMs);
sprang57c2fff2017-01-16 06:24:02 -08002052
2053 // Not called on second frame.
2054 EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(expected_bitrate))
2055 .Times(0);
2056 video_source_.IncomingCapturedFrame(
2057 CreateFrame(kStartTimeMs + 1, codec_width_, codec_height_));
sprang4847ae62017-06-27 07:06:52 -07002058 WaitForEncodedFrame(kStartTimeMs + 1);
sprang57c2fff2017-01-16 06:24:02 -08002059
2060 // Called after a process interval.
2061 const int64_t kProcessIntervalMs =
2062 vcm::VCMProcessTimer::kDefaultProcessIntervalMs;
sprang4847ae62017-06-27 07:06:52 -07002063 fake_clock_.AdvanceTimeMicros(rtc::kNumMicrosecsPerMillisec *
2064 (kProcessIntervalMs + (1000 / kDefaultFps)));
sprang57c2fff2017-01-16 06:24:02 -08002065 EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(expected_bitrate))
2066 .Times(1);
2067 video_source_.IncomingCapturedFrame(CreateFrame(
2068 kStartTimeMs + kProcessIntervalMs, codec_width_, codec_height_));
sprang4847ae62017-06-27 07:06:52 -07002069 WaitForEncodedFrame(kStartTimeMs + kProcessIntervalMs);
sprang57c2fff2017-01-16 06:24:02 -08002070
mflodmancc3d4422017-08-03 08:27:51 -07002071 video_stream_encoder_->Stop();
sprang57c2fff2017-01-16 06:24:02 -08002072}
2073
Niels Möller7dc26b72017-12-06 10:27:48 +01002074TEST_F(VideoStreamEncoderTest, OveruseDetectorUpdatedOnReconfigureAndAdaption) {
2075 const int kFrameWidth = 1280;
2076 const int kFrameHeight = 720;
2077 const int kFramerate = 24;
2078
2079 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
2080 test::FrameForwarder source;
2081 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002082 &source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
Niels Möller7dc26b72017-12-06 10:27:48 +01002083
2084 // Insert a single frame, triggering initial configuration.
2085 source.IncomingCapturedFrame(CreateFrame(1, kFrameWidth, kFrameHeight));
2086 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2087
2088 EXPECT_EQ(
2089 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2090 kDefaultFramerate);
2091
2092 // Trigger reconfigure encoder (without resetting the entire instance).
2093 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +02002094 video_encoder_config.codec_type = kVideoCodecVP8;
Niels Möller7dc26b72017-12-06 10:27:48 +01002095 video_encoder_config.max_bitrate_bps = kTargetBitrateBps;
2096 video_encoder_config.number_of_streams = 1;
2097 video_encoder_config.video_stream_factory =
2098 new rtc::RefCountedObject<VideoStreamFactory>(1, kFramerate);
2099 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02002100 kMaxPayloadLength);
Niels Möller7dc26b72017-12-06 10:27:48 +01002101 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2102
2103 // Detector should be updated with fps limit from codec config.
2104 EXPECT_EQ(
2105 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2106 kFramerate);
2107
2108 // Trigger overuse, max framerate should be reduced.
2109 VideoSendStream::Stats stats = stats_proxy_->GetStats();
2110 stats.input_frame_rate = kFramerate;
2111 stats_proxy_->SetMockStats(stats);
2112 video_stream_encoder_->TriggerCpuOveruse();
2113 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2114 int adapted_framerate =
2115 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate();
2116 EXPECT_LT(adapted_framerate, kFramerate);
2117
2118 // Trigger underuse, max framerate should go back to codec configured fps.
2119 // Set extra low fps, to make sure it's actually reset, not just incremented.
2120 stats = stats_proxy_->GetStats();
2121 stats.input_frame_rate = adapted_framerate / 2;
2122 stats_proxy_->SetMockStats(stats);
2123 video_stream_encoder_->TriggerCpuNormalUsage();
2124 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2125 EXPECT_EQ(
2126 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2127 kFramerate);
2128
2129 video_stream_encoder_->Stop();
2130}
2131
2132TEST_F(VideoStreamEncoderTest,
2133 OveruseDetectorUpdatedRespectsFramerateAfterUnderuse) {
2134 const int kFrameWidth = 1280;
2135 const int kFrameHeight = 720;
2136 const int kLowFramerate = 15;
2137 const int kHighFramerate = 25;
2138
2139 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
2140 test::FrameForwarder source;
2141 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002142 &source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
Niels Möller7dc26b72017-12-06 10:27:48 +01002143
2144 // Trigger initial configuration.
2145 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +02002146 video_encoder_config.codec_type = kVideoCodecVP8;
Niels Möller7dc26b72017-12-06 10:27:48 +01002147 video_encoder_config.max_bitrate_bps = kTargetBitrateBps;
2148 video_encoder_config.number_of_streams = 1;
2149 video_encoder_config.video_stream_factory =
2150 new rtc::RefCountedObject<VideoStreamFactory>(1, kLowFramerate);
2151 source.IncomingCapturedFrame(CreateFrame(1, kFrameWidth, kFrameHeight));
2152 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02002153 kMaxPayloadLength);
Niels Möller7dc26b72017-12-06 10:27:48 +01002154 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2155
2156 EXPECT_EQ(
2157 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2158 kLowFramerate);
2159
2160 // Trigger overuse, max framerate should be reduced.
2161 VideoSendStream::Stats stats = stats_proxy_->GetStats();
2162 stats.input_frame_rate = kLowFramerate;
2163 stats_proxy_->SetMockStats(stats);
2164 video_stream_encoder_->TriggerCpuOveruse();
2165 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2166 int adapted_framerate =
2167 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate();
2168 EXPECT_LT(adapted_framerate, kLowFramerate);
2169
2170 // Reconfigure the encoder with a new (higher max framerate), max fps should
2171 // still respect the adaptation.
2172 video_encoder_config.video_stream_factory =
2173 new rtc::RefCountedObject<VideoStreamFactory>(1, kHighFramerate);
2174 source.IncomingCapturedFrame(CreateFrame(1, kFrameWidth, kFrameHeight));
2175 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02002176 kMaxPayloadLength);
Niels Möller7dc26b72017-12-06 10:27:48 +01002177 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2178
2179 EXPECT_EQ(
2180 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2181 adapted_framerate);
2182
2183 // Trigger underuse, max framerate should go back to codec configured fps.
2184 stats = stats_proxy_->GetStats();
2185 stats.input_frame_rate = adapted_framerate;
2186 stats_proxy_->SetMockStats(stats);
2187 video_stream_encoder_->TriggerCpuNormalUsage();
2188 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
2189 EXPECT_EQ(
2190 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2191 kHighFramerate);
2192
2193 video_stream_encoder_->Stop();
2194}
2195
mflodmancc3d4422017-08-03 08:27:51 -07002196TEST_F(VideoStreamEncoderTest,
2197 OveruseDetectorUpdatedOnDegradationPreferenceChange) {
sprangfda496a2017-06-15 04:21:07 -07002198 const int kFrameWidth = 1280;
2199 const int kFrameHeight = 720;
2200 const int kFramerate = 24;
2201
mflodmancc3d4422017-08-03 08:27:51 -07002202 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
sprangfda496a2017-06-15 04:21:07 -07002203 test::FrameForwarder source;
mflodmancc3d4422017-08-03 08:27:51 -07002204 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002205 &source, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
sprangfda496a2017-06-15 04:21:07 -07002206
2207 // Trigger initial configuration.
2208 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +02002209 video_encoder_config.codec_type = kVideoCodecVP8;
sprangfda496a2017-06-15 04:21:07 -07002210 video_encoder_config.max_bitrate_bps = kTargetBitrateBps;
2211 video_encoder_config.number_of_streams = 1;
2212 video_encoder_config.video_stream_factory =
2213 new rtc::RefCountedObject<VideoStreamFactory>(1, kFramerate);
2214 source.IncomingCapturedFrame(CreateFrame(1, kFrameWidth, kFrameHeight));
mflodmancc3d4422017-08-03 08:27:51 -07002215 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02002216 kMaxPayloadLength);
mflodmancc3d4422017-08-03 08:27:51 -07002217 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
sprangfda496a2017-06-15 04:21:07 -07002218
Niels Möller7dc26b72017-12-06 10:27:48 +01002219 EXPECT_EQ(
2220 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2221 kFramerate);
sprangfda496a2017-06-15 04:21:07 -07002222
2223 // Trigger overuse, max framerate should be reduced.
2224 VideoSendStream::Stats stats = stats_proxy_->GetStats();
2225 stats.input_frame_rate = kFramerate;
2226 stats_proxy_->SetMockStats(stats);
mflodmancc3d4422017-08-03 08:27:51 -07002227 video_stream_encoder_->TriggerCpuOveruse();
2228 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
Niels Möller7dc26b72017-12-06 10:27:48 +01002229 int adapted_framerate =
2230 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate();
sprangfda496a2017-06-15 04:21:07 -07002231 EXPECT_LT(adapted_framerate, kFramerate);
2232
2233 // Change degradation preference to not enable framerate scaling. Target
2234 // framerate should be changed to codec defined limit.
mflodmancc3d4422017-08-03 08:27:51 -07002235 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002236 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
mflodmancc3d4422017-08-03 08:27:51 -07002237 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
Niels Möller7dc26b72017-12-06 10:27:48 +01002238 EXPECT_EQ(
2239 video_stream_encoder_->overuse_detector_proxy_->GetLastTargetFramerate(),
2240 kFramerate);
sprangfda496a2017-06-15 04:21:07 -07002241
mflodmancc3d4422017-08-03 08:27:51 -07002242 video_stream_encoder_->Stop();
sprangfda496a2017-06-15 04:21:07 -07002243}
2244
mflodmancc3d4422017-08-03 08:27:51 -07002245TEST_F(VideoStreamEncoderTest, DropsFramesAndScalesWhenBitrateIsTooLow) {
asaperssonfab67072017-04-04 05:51:49 -07002246 const int kTooLowBitrateForFrameSizeBps = 10000;
mflodmancc3d4422017-08-03 08:27:51 -07002247 video_stream_encoder_->OnBitrateUpdated(kTooLowBitrateForFrameSizeBps, 0, 0);
asaperssonfab67072017-04-04 05:51:49 -07002248 const int kWidth = 640;
2249 const int kHeight = 360;
kthelgason2bc68642017-02-07 07:02:22 -08002250
asaperssonfab67072017-04-04 05:51:49 -07002251 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
kthelgason2bc68642017-02-07 07:02:22 -08002252
2253 // Expect to drop this frame, the wait should time out.
sprang4847ae62017-06-27 07:06:52 -07002254 ExpectDroppedFrame();
kthelgason2bc68642017-02-07 07:02:22 -08002255
2256 // Expect the sink_wants to specify a scaled frame.
asapersson0944a802017-04-07 00:57:58 -07002257 EXPECT_LT(video_source_.sink_wants().max_pixel_count, kWidth * kHeight);
kthelgason2bc68642017-02-07 07:02:22 -08002258
sprangc5d62e22017-04-02 23:53:04 -07002259 int last_pixel_count = video_source_.sink_wants().max_pixel_count;
kthelgason2bc68642017-02-07 07:02:22 -08002260
asaperssonfab67072017-04-04 05:51:49 -07002261 // Next frame is scaled.
kthelgason2bc68642017-02-07 07:02:22 -08002262 video_source_.IncomingCapturedFrame(
asaperssonfab67072017-04-04 05:51:49 -07002263 CreateFrame(2, kWidth * 3 / 4, kHeight * 3 / 4));
kthelgason2bc68642017-02-07 07:02:22 -08002264
2265 // Expect to drop this frame, the wait should time out.
sprang4847ae62017-06-27 07:06:52 -07002266 ExpectDroppedFrame();
kthelgason2bc68642017-02-07 07:02:22 -08002267
sprangc5d62e22017-04-02 23:53:04 -07002268 EXPECT_LT(video_source_.sink_wants().max_pixel_count, last_pixel_count);
kthelgason2bc68642017-02-07 07:02:22 -08002269
mflodmancc3d4422017-08-03 08:27:51 -07002270 video_stream_encoder_->Stop();
kthelgason2bc68642017-02-07 07:02:22 -08002271}
2272
mflodmancc3d4422017-08-03 08:27:51 -07002273TEST_F(VideoStreamEncoderTest,
2274 NumberOfDroppedFramesLimitedWhenBitrateIsTooLow) {
asaperssonfab67072017-04-04 05:51:49 -07002275 const int kTooLowBitrateForFrameSizeBps = 10000;
mflodmancc3d4422017-08-03 08:27:51 -07002276 video_stream_encoder_->OnBitrateUpdated(kTooLowBitrateForFrameSizeBps, 0, 0);
asaperssonfab67072017-04-04 05:51:49 -07002277 const int kWidth = 640;
2278 const int kHeight = 360;
kthelgason2bc68642017-02-07 07:02:22 -08002279
2280 // We expect the n initial frames to get dropped.
2281 int i;
2282 for (i = 1; i <= kMaxInitialFramedrop; ++i) {
asaperssonfab67072017-04-04 05:51:49 -07002283 video_source_.IncomingCapturedFrame(CreateFrame(i, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002284 ExpectDroppedFrame();
kthelgason2bc68642017-02-07 07:02:22 -08002285 }
2286 // The n+1th frame should not be dropped, even though it's size is too large.
asaperssonfab67072017-04-04 05:51:49 -07002287 video_source_.IncomingCapturedFrame(CreateFrame(i, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002288 WaitForEncodedFrame(i);
kthelgason2bc68642017-02-07 07:02:22 -08002289
2290 // Expect the sink_wants to specify a scaled frame.
asaperssonfab67072017-04-04 05:51:49 -07002291 EXPECT_LT(video_source_.sink_wants().max_pixel_count, kWidth * kHeight);
kthelgason2bc68642017-02-07 07:02:22 -08002292
mflodmancc3d4422017-08-03 08:27:51 -07002293 video_stream_encoder_->Stop();
kthelgason2bc68642017-02-07 07:02:22 -08002294}
2295
mflodmancc3d4422017-08-03 08:27:51 -07002296TEST_F(VideoStreamEncoderTest,
2297 InitialFrameDropOffWithMaintainResolutionPreference) {
asaperssonfab67072017-04-04 05:51:49 -07002298 const int kWidth = 640;
2299 const int kHeight = 360;
mflodmancc3d4422017-08-03 08:27:51 -07002300 video_stream_encoder_->OnBitrateUpdated(kLowTargetBitrateBps, 0, 0);
kthelgason2bc68642017-02-07 07:02:22 -08002301
2302 // Set degradation preference.
mflodmancc3d4422017-08-03 08:27:51 -07002303 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002304 &video_source_, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
kthelgason2bc68642017-02-07 07:02:22 -08002305
asaperssonfab67072017-04-04 05:51:49 -07002306 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
kthelgason2bc68642017-02-07 07:02:22 -08002307 // Frame should not be dropped, even if it's too large.
sprang4847ae62017-06-27 07:06:52 -07002308 WaitForEncodedFrame(1);
kthelgason2bc68642017-02-07 07:02:22 -08002309
mflodmancc3d4422017-08-03 08:27:51 -07002310 video_stream_encoder_->Stop();
kthelgason2bc68642017-02-07 07:02:22 -08002311}
2312
mflodmancc3d4422017-08-03 08:27:51 -07002313TEST_F(VideoStreamEncoderTest, InitialFrameDropOffWhenEncoderDisabledScaling) {
asaperssonfab67072017-04-04 05:51:49 -07002314 const int kWidth = 640;
2315 const int kHeight = 360;
kthelgasonad9010c2017-02-14 00:46:51 -08002316 fake_encoder_.SetQualityScaling(false);
Niels Möller4db138e2018-04-19 09:04:13 +02002317
2318 VideoEncoderConfig video_encoder_config;
2319 test::FillEncoderConfiguration(kVideoCodecVP8, 1, &video_encoder_config);
2320 // Make format different, to force recreation of encoder.
2321 video_encoder_config.video_format.parameters["foo"] = "foo";
2322 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02002323 kMaxPayloadLength);
mflodmancc3d4422017-08-03 08:27:51 -07002324 video_stream_encoder_->OnBitrateUpdated(kLowTargetBitrateBps, 0, 0);
asapersson09f05612017-05-15 23:40:18 -07002325
kthelgasonb83797b2017-02-14 11:57:25 -08002326 // Force quality scaler reconfiguration by resetting the source.
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002327 video_stream_encoder_->SetSource(&video_source_,
2328 webrtc::DegradationPreference::BALANCED);
kthelgasonad9010c2017-02-14 00:46:51 -08002329
asaperssonfab67072017-04-04 05:51:49 -07002330 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
kthelgasonad9010c2017-02-14 00:46:51 -08002331 // Frame should not be dropped, even if it's too large.
sprang4847ae62017-06-27 07:06:52 -07002332 WaitForEncodedFrame(1);
kthelgasonad9010c2017-02-14 00:46:51 -08002333
mflodmancc3d4422017-08-03 08:27:51 -07002334 video_stream_encoder_->Stop();
kthelgasonad9010c2017-02-14 00:46:51 -08002335 fake_encoder_.SetQualityScaling(true);
2336}
2337
Kári Tristan Helgason639602a2018-08-02 10:51:40 +02002338TEST_F(VideoStreamEncoderTest, InitialFrameDropActivatesWhenBWEstimateReady) {
2339 webrtc::test::ScopedFieldTrials field_trials(
2340 "WebRTC-InitialFramedrop/Enabled/");
2341 // Reset encoder for field trials to take effect.
2342 ConfigureEncoder(video_encoder_config_.Copy());
2343 const int kTooLowBitrateForFrameSizeBps = 10000;
2344 const int kWidth = 640;
2345 const int kHeight = 360;
2346
2347 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
2348 video_source_.IncomingCapturedFrame(CreateFrame(1, kWidth, kHeight));
2349 // Frame should not be dropped.
2350 WaitForEncodedFrame(1);
2351
2352 video_stream_encoder_->OnBitrateUpdated(kTooLowBitrateForFrameSizeBps, 0, 0);
2353 video_source_.IncomingCapturedFrame(CreateFrame(2, kWidth, kHeight));
2354 // Expect to drop this frame, the wait should time out.
2355 ExpectDroppedFrame();
2356
2357 // Expect the sink_wants to specify a scaled frame.
2358 EXPECT_LT(video_source_.sink_wants().max_pixel_count, kWidth * kHeight);
2359 video_stream_encoder_->Stop();
2360}
2361
mflodmancc3d4422017-08-03 08:27:51 -07002362TEST_F(VideoStreamEncoderTest,
asaperssond0de2952017-04-21 01:47:31 -07002363 ResolutionNotAdaptedForTooSmallFrame_MaintainFramerateMode) {
2364 const int kTooSmallWidth = 10;
2365 const int kTooSmallHeight = 10;
mflodmancc3d4422017-08-03 08:27:51 -07002366 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssond0de2952017-04-21 01:47:31 -07002367
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002368 // Enable MAINTAIN_FRAMERATE preference, no initial limitation.
asaperssond0de2952017-04-21 01:47:31 -07002369 test::FrameForwarder source;
mflodmancc3d4422017-08-03 08:27:51 -07002370 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002371 &source, webrtc::DegradationPreference::MAINTAIN_FRAMERATE);
asaperssond0de2952017-04-21 01:47:31 -07002372 VerifyNoLimitation(source.sink_wants());
2373 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2374
2375 // Trigger adapt down, too small frame, expect no change.
2376 source.IncomingCapturedFrame(CreateFrame(1, kTooSmallWidth, kTooSmallHeight));
sprang4847ae62017-06-27 07:06:52 -07002377 WaitForEncodedFrame(1);
mflodmancc3d4422017-08-03 08:27:51 -07002378 video_stream_encoder_->TriggerCpuOveruse();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002379 VerifyNoLimitation(source.sink_wants());
asaperssond0de2952017-04-21 01:47:31 -07002380 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2381 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2382
mflodmancc3d4422017-08-03 08:27:51 -07002383 video_stream_encoder_->Stop();
asaperssond0de2952017-04-21 01:47:31 -07002384}
2385
mflodmancc3d4422017-08-03 08:27:51 -07002386TEST_F(VideoStreamEncoderTest,
2387 ResolutionNotAdaptedForTooSmallFrame_BalancedMode) {
asaperssonf7e294d2017-06-13 23:25:22 -07002388 const int kTooSmallWidth = 10;
2389 const int kTooSmallHeight = 10;
2390 const int kFpsLimit = 7;
mflodmancc3d4422017-08-03 08:27:51 -07002391 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07002392
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002393 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07002394 test::FrameForwarder source;
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002395 video_stream_encoder_->SetSource(&source,
2396 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07002397 VerifyNoLimitation(source.sink_wants());
2398 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2399 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2400
2401 // Trigger adapt down, expect limited framerate.
2402 source.IncomingCapturedFrame(CreateFrame(1, kTooSmallWidth, kTooSmallHeight));
sprang4847ae62017-06-27 07:06:52 -07002403 WaitForEncodedFrame(1);
mflodmancc3d4422017-08-03 08:27:51 -07002404 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002405 VerifyFpsEqResolutionMax(source.sink_wants(), kFpsLimit);
2406 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2407 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2408 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2409
2410 // Trigger adapt down, too small frame, expect no change.
2411 source.IncomingCapturedFrame(CreateFrame(2, kTooSmallWidth, kTooSmallHeight));
sprang4847ae62017-06-27 07:06:52 -07002412 WaitForEncodedFrame(2);
mflodmancc3d4422017-08-03 08:27:51 -07002413 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002414 VerifyFpsEqResolutionMax(source.sink_wants(), kFpsLimit);
2415 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2416 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2417 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2418
mflodmancc3d4422017-08-03 08:27:51 -07002419 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07002420}
2421
mflodmancc3d4422017-08-03 08:27:51 -07002422TEST_F(VideoStreamEncoderTest, FailingInitEncodeDoesntCauseCrash) {
asapersson02465b82017-04-10 01:12:52 -07002423 fake_encoder_.ForceInitEncodeFailure(true);
mflodmancc3d4422017-08-03 08:27:51 -07002424 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
Niels Möllerf1338562018-04-26 09:51:47 +02002425 ResetEncoder("VP8", 2, 1, 1, false);
asapersson02465b82017-04-10 01:12:52 -07002426 const int kFrameWidth = 1280;
2427 const int kFrameHeight = 720;
2428 video_source_.IncomingCapturedFrame(
2429 CreateFrame(1, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002430 ExpectDroppedFrame();
mflodmancc3d4422017-08-03 08:27:51 -07002431 video_stream_encoder_->Stop();
asapersson02465b82017-04-10 01:12:52 -07002432}
2433
sprangb1ca0732017-02-01 08:38:12 -08002434// TODO(sprang): Extend this with fps throttling and any "balanced" extensions.
mflodmancc3d4422017-08-03 08:27:51 -07002435TEST_F(VideoStreamEncoderTest,
2436 AdaptsResolutionOnOveruse_MaintainFramerateMode) {
2437 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
sprangb1ca0732017-02-01 08:38:12 -08002438
2439 const int kFrameWidth = 1280;
2440 const int kFrameHeight = 720;
2441 // Enabled default VideoAdapter downscaling. First step is 3/4, not 3/5 as
mflodmancc3d4422017-08-03 08:27:51 -07002442 // requested by
2443 // VideoStreamEncoder::VideoSourceProxy::RequestResolutionLowerThan().
sprangb1ca0732017-02-01 08:38:12 -08002444 video_source_.set_adaptation_enabled(true);
2445
2446 video_source_.IncomingCapturedFrame(
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002447 CreateFrame(1, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002448 WaitForEncodedFrame(kFrameWidth, kFrameHeight);
sprangb1ca0732017-02-01 08:38:12 -08002449
2450 // Trigger CPU overuse, downscale by 3/4.
mflodmancc3d4422017-08-03 08:27:51 -07002451 video_stream_encoder_->TriggerCpuOveruse();
sprangb1ca0732017-02-01 08:38:12 -08002452 video_source_.IncomingCapturedFrame(
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002453 CreateFrame(2, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002454 WaitForEncodedFrame((kFrameWidth * 3) / 4, (kFrameHeight * 3) / 4);
sprangb1ca0732017-02-01 08:38:12 -08002455
asaperssonfab67072017-04-04 05:51:49 -07002456 // Trigger CPU normal use, return to original resolution.
mflodmancc3d4422017-08-03 08:27:51 -07002457 video_stream_encoder_->TriggerCpuNormalUsage();
sprangb1ca0732017-02-01 08:38:12 -08002458 video_source_.IncomingCapturedFrame(
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002459 CreateFrame(3, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002460 WaitForEncodedFrame(kFrameWidth, kFrameHeight);
sprangb1ca0732017-02-01 08:38:12 -08002461
mflodmancc3d4422017-08-03 08:27:51 -07002462 video_stream_encoder_->Stop();
sprangb1ca0732017-02-01 08:38:12 -08002463}
sprangfe627f32017-03-29 08:24:59 -07002464
mflodmancc3d4422017-08-03 08:27:51 -07002465TEST_F(VideoStreamEncoderTest,
2466 AdaptsFramerateOnOveruse_MaintainResolutionMode) {
sprangc5d62e22017-04-02 23:53:04 -07002467 const int kFrameWidth = 1280;
2468 const int kFrameHeight = 720;
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002469 int kFrameIntervalMs = rtc::kNumMillisecsPerSec / max_framerate_;
sprangc5d62e22017-04-02 23:53:04 -07002470
mflodmancc3d4422017-08-03 08:27:51 -07002471 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
2472 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002473 &video_source_, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
sprangc5d62e22017-04-02 23:53:04 -07002474 video_source_.set_adaptation_enabled(true);
2475
sprang4847ae62017-06-27 07:06:52 -07002476 int64_t timestamp_ms = fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec;
sprangc5d62e22017-04-02 23:53:04 -07002477
2478 video_source_.IncomingCapturedFrame(
2479 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002480 WaitForEncodedFrame(timestamp_ms);
sprangc5d62e22017-04-02 23:53:04 -07002481
2482 // Try to trigger overuse. No fps estimate available => no effect.
mflodmancc3d4422017-08-03 08:27:51 -07002483 video_stream_encoder_->TriggerCpuOveruse();
sprangc5d62e22017-04-02 23:53:04 -07002484
2485 // Insert frames for one second to get a stable estimate.
sprang4847ae62017-06-27 07:06:52 -07002486 for (int i = 0; i < max_framerate_; ++i) {
sprangc5d62e22017-04-02 23:53:04 -07002487 timestamp_ms += kFrameIntervalMs;
sprangc5d62e22017-04-02 23:53:04 -07002488 video_source_.IncomingCapturedFrame(
2489 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002490 WaitForEncodedFrame(timestamp_ms);
sprangc5d62e22017-04-02 23:53:04 -07002491 }
2492
2493 // Trigger CPU overuse, reduce framerate by 2/3.
mflodmancc3d4422017-08-03 08:27:51 -07002494 video_stream_encoder_->TriggerCpuOveruse();
sprangc5d62e22017-04-02 23:53:04 -07002495 int num_frames_dropped = 0;
sprang4847ae62017-06-27 07:06:52 -07002496 for (int i = 0; i < max_framerate_; ++i) {
sprangc5d62e22017-04-02 23:53:04 -07002497 timestamp_ms += kFrameIntervalMs;
sprangc5d62e22017-04-02 23:53:04 -07002498 video_source_.IncomingCapturedFrame(
2499 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002500 if (!WaitForFrame(kFrameTimeoutMs)) {
sprangc5d62e22017-04-02 23:53:04 -07002501 ++num_frames_dropped;
2502 } else {
Åsa Perssonc74d8da2017-12-04 14:13:56 +01002503 sink_.CheckLastFrameSizeMatches(kFrameWidth, kFrameHeight);
sprangc5d62e22017-04-02 23:53:04 -07002504 }
2505 }
2506
sprang4847ae62017-06-27 07:06:52 -07002507 // Add some slack to account for frames dropped by the frame dropper.
2508 const int kErrorMargin = 1;
2509 EXPECT_NEAR(num_frames_dropped, max_framerate_ - (max_framerate_ * 2 / 3),
sprangc5d62e22017-04-02 23:53:04 -07002510 kErrorMargin);
2511
2512 // Trigger CPU overuse, reduce framerate by 2/3 again.
mflodmancc3d4422017-08-03 08:27:51 -07002513 video_stream_encoder_->TriggerCpuOveruse();
sprangc5d62e22017-04-02 23:53:04 -07002514 num_frames_dropped = 0;
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002515 for (int i = 0; i < max_framerate_; ++i) {
sprangc5d62e22017-04-02 23:53:04 -07002516 timestamp_ms += kFrameIntervalMs;
sprangc5d62e22017-04-02 23:53:04 -07002517 video_source_.IncomingCapturedFrame(
2518 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002519 if (!WaitForFrame(kFrameTimeoutMs)) {
sprangc5d62e22017-04-02 23:53:04 -07002520 ++num_frames_dropped;
2521 } else {
Åsa Perssonc74d8da2017-12-04 14:13:56 +01002522 sink_.CheckLastFrameSizeMatches(kFrameWidth, kFrameHeight);
sprangc5d62e22017-04-02 23:53:04 -07002523 }
2524 }
sprang4847ae62017-06-27 07:06:52 -07002525 EXPECT_NEAR(num_frames_dropped, max_framerate_ - (max_framerate_ * 4 / 9),
sprangc5d62e22017-04-02 23:53:04 -07002526 kErrorMargin);
2527
2528 // Go back up one step.
mflodmancc3d4422017-08-03 08:27:51 -07002529 video_stream_encoder_->TriggerCpuNormalUsage();
sprangc5d62e22017-04-02 23:53:04 -07002530 num_frames_dropped = 0;
sprang4847ae62017-06-27 07:06:52 -07002531 for (int i = 0; i < max_framerate_; ++i) {
sprangc5d62e22017-04-02 23:53:04 -07002532 timestamp_ms += kFrameIntervalMs;
sprangc5d62e22017-04-02 23:53:04 -07002533 video_source_.IncomingCapturedFrame(
2534 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002535 if (!WaitForFrame(kFrameTimeoutMs)) {
sprangc5d62e22017-04-02 23:53:04 -07002536 ++num_frames_dropped;
2537 } else {
Åsa Perssonc74d8da2017-12-04 14:13:56 +01002538 sink_.CheckLastFrameSizeMatches(kFrameWidth, kFrameHeight);
sprangc5d62e22017-04-02 23:53:04 -07002539 }
2540 }
sprang4847ae62017-06-27 07:06:52 -07002541 EXPECT_NEAR(num_frames_dropped, max_framerate_ - (max_framerate_ * 2 / 3),
sprangc5d62e22017-04-02 23:53:04 -07002542 kErrorMargin);
2543
2544 // Go back up to original mode.
mflodmancc3d4422017-08-03 08:27:51 -07002545 video_stream_encoder_->TriggerCpuNormalUsage();
sprangc5d62e22017-04-02 23:53:04 -07002546 num_frames_dropped = 0;
sprang4847ae62017-06-27 07:06:52 -07002547 for (int i = 0; i < max_framerate_; ++i) {
sprangc5d62e22017-04-02 23:53:04 -07002548 timestamp_ms += kFrameIntervalMs;
sprangc5d62e22017-04-02 23:53:04 -07002549 video_source_.IncomingCapturedFrame(
2550 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07002551 if (!WaitForFrame(kFrameTimeoutMs)) {
sprangc5d62e22017-04-02 23:53:04 -07002552 ++num_frames_dropped;
2553 } else {
Åsa Perssonc74d8da2017-12-04 14:13:56 +01002554 sink_.CheckLastFrameSizeMatches(kFrameWidth, kFrameHeight);
sprangc5d62e22017-04-02 23:53:04 -07002555 }
2556 }
2557 EXPECT_NEAR(num_frames_dropped, 0, kErrorMargin);
2558
mflodmancc3d4422017-08-03 08:27:51 -07002559 video_stream_encoder_->Stop();
sprangc5d62e22017-04-02 23:53:04 -07002560}
2561
mflodmancc3d4422017-08-03 08:27:51 -07002562TEST_F(VideoStreamEncoderTest, DoesntAdaptDownPastMinFramerate) {
sprangc5d62e22017-04-02 23:53:04 -07002563 const int kFramerateFps = 5;
2564 const int kFrameIntervalMs = rtc::kNumMillisecsPerSec / kFramerateFps;
sprangc5d62e22017-04-02 23:53:04 -07002565 const int kFrameWidth = 1280;
2566 const int kFrameHeight = 720;
2567
sprang4847ae62017-06-27 07:06:52 -07002568 // Reconfigure encoder with two temporal layers and screensharing, which will
2569 // disable frame dropping and make testing easier.
Niels Möllerf1338562018-04-26 09:51:47 +02002570 ResetEncoder("VP8", 1, 2, 1, true);
sprang4847ae62017-06-27 07:06:52 -07002571
mflodmancc3d4422017-08-03 08:27:51 -07002572 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
2573 video_stream_encoder_->SetSource(
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002574 &video_source_, webrtc::DegradationPreference::MAINTAIN_RESOLUTION);
sprangc5d62e22017-04-02 23:53:04 -07002575 video_source_.set_adaptation_enabled(true);
2576
sprang4847ae62017-06-27 07:06:52 -07002577 int64_t timestamp_ms = fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec;
sprangc5d62e22017-04-02 23:53:04 -07002578
2579 // Trigger overuse as much as we can.
Jonathan Yubc771b72017-12-08 17:04:29 -08002580 rtc::VideoSinkWants last_wants;
2581 do {
2582 last_wants = video_source_.sink_wants();
2583
sprangc5d62e22017-04-02 23:53:04 -07002584 // Insert frames to get a new fps estimate...
2585 for (int j = 0; j < kFramerateFps; ++j) {
2586 video_source_.IncomingCapturedFrame(
2587 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
Jonathan Yubc771b72017-12-08 17:04:29 -08002588 if (video_source_.last_sent_width()) {
2589 sink_.WaitForEncodedFrame(timestamp_ms);
2590 }
sprangc5d62e22017-04-02 23:53:04 -07002591 timestamp_ms += kFrameIntervalMs;
Yves Gerey665174f2018-06-19 15:03:05 +02002592 fake_clock_.AdvanceTimeMicros(kFrameIntervalMs *
2593 rtc::kNumMicrosecsPerMillisec);
sprangc5d62e22017-04-02 23:53:04 -07002594 }
2595 // ...and then try to adapt again.
mflodmancc3d4422017-08-03 08:27:51 -07002596 video_stream_encoder_->TriggerCpuOveruse();
Jonathan Yubc771b72017-12-08 17:04:29 -08002597 } while (video_source_.sink_wants().max_framerate_fps <
2598 last_wants.max_framerate_fps);
sprangc5d62e22017-04-02 23:53:04 -07002599
Jonathan Yubc771b72017-12-08 17:04:29 -08002600 VerifyFpsEqResolutionMax(video_source_.sink_wants(), kMinFramerateFps);
asaperssonf7e294d2017-06-13 23:25:22 -07002601
mflodmancc3d4422017-08-03 08:27:51 -07002602 video_stream_encoder_->Stop();
sprangc5d62e22017-04-02 23:53:04 -07002603}
asaperssonf7e294d2017-06-13 23:25:22 -07002604
mflodmancc3d4422017-08-03 08:27:51 -07002605TEST_F(VideoStreamEncoderTest,
2606 AdaptsResolutionAndFramerateForLowQuality_BalancedMode) {
asaperssonf7e294d2017-06-13 23:25:22 -07002607 const int kWidth = 1280;
2608 const int kHeight = 720;
2609 const int64_t kFrameIntervalMs = 150;
2610 int64_t timestamp_ms = kFrameIntervalMs;
mflodmancc3d4422017-08-03 08:27:51 -07002611 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07002612
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002613 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07002614 AdaptingFrameForwarder source;
2615 source.set_adaptation_enabled(true);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002616 video_stream_encoder_->SetSource(&source,
2617 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07002618 timestamp_ms += kFrameIntervalMs;
2619 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002620 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002621 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002622 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2623 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2624 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2625
2626 // Trigger adapt down, expect scaled down resolution (960x540@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002627 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002628 timestamp_ms += kFrameIntervalMs;
2629 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002630 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002631 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
2632 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2633 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2634 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2635
2636 // Trigger adapt down, expect scaled down resolution (640x360@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002637 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002638 timestamp_ms += kFrameIntervalMs;
2639 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002640 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002641 VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
2642 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2643 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2644 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2645
2646 // Trigger adapt down, expect reduced fps (640x360@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002647 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002648 timestamp_ms += kFrameIntervalMs;
2649 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002650 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002651 VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
2652 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2653 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2654 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2655
2656 // Trigger adapt down, expect scaled down resolution (480x270@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002657 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002658 timestamp_ms += kFrameIntervalMs;
2659 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002660 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002661 VerifyFpsEqResolutionLt(source.sink_wants(), source.last_wants());
2662 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2663 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2664 EXPECT_EQ(4, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2665
2666 // Restrict bitrate, trigger adapt down, expect reduced fps (480x270@10fps).
mflodmancc3d4422017-08-03 08:27:51 -07002667 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002668 timestamp_ms += kFrameIntervalMs;
2669 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002670 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002671 VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
2672 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2673 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2674 EXPECT_EQ(5, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2675
2676 // Trigger adapt down, expect scaled down resolution (320x180@10fps).
mflodmancc3d4422017-08-03 08:27:51 -07002677 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002678 timestamp_ms += kFrameIntervalMs;
2679 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002680 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002681 VerifyFpsEqResolutionLt(source.sink_wants(), source.last_wants());
2682 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2683 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2684 EXPECT_EQ(6, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2685
2686 // Trigger adapt down, expect reduced fps (320x180@7fps).
mflodmancc3d4422017-08-03 08:27:51 -07002687 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002688 timestamp_ms += kFrameIntervalMs;
2689 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002690 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002691 VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
2692 rtc::VideoSinkWants last_wants = source.sink_wants();
2693 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2694 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2695 EXPECT_EQ(7, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2696
2697 // Trigger adapt down, min resolution reached, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07002698 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002699 timestamp_ms += kFrameIntervalMs;
2700 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002701 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002702 VerifyFpsEqResolutionEq(source.sink_wants(), last_wants);
2703 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2704 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2705 EXPECT_EQ(7, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2706
2707 // Trigger adapt down, expect expect increased fps (320x180@10fps).
mflodmancc3d4422017-08-03 08:27:51 -07002708 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002709 timestamp_ms += kFrameIntervalMs;
2710 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002711 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002712 VerifyFpsGtResolutionEq(source.sink_wants(), source.last_wants());
2713 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2714 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2715 EXPECT_EQ(8, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2716
2717 // Trigger adapt up, expect upscaled resolution (480x270@10fps).
mflodmancc3d4422017-08-03 08:27:51 -07002718 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002719 timestamp_ms += kFrameIntervalMs;
2720 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002721 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002722 VerifyFpsEqResolutionGt(source.sink_wants(), source.last_wants());
2723 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2724 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2725 EXPECT_EQ(9, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2726
2727 // Increase bitrate, trigger adapt up, expect increased fps (480x270@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002728 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002729 timestamp_ms += kFrameIntervalMs;
2730 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002731 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002732 VerifyFpsGtResolutionEq(source.sink_wants(), source.last_wants());
2733 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2734 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2735 EXPECT_EQ(10, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2736
2737 // Trigger adapt up, expect upscaled resolution (640x360@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002738 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002739 timestamp_ms += kFrameIntervalMs;
2740 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002741 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002742 VerifyFpsEqResolutionGt(source.sink_wants(), source.last_wants());
2743 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2744 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2745 EXPECT_EQ(11, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2746
2747 // Trigger adapt up, expect increased fps (640x360@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002748 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002749 timestamp_ms += kFrameIntervalMs;
2750 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002751 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002752 VerifyFpsMaxResolutionEq(source.sink_wants(), source.last_wants());
2753 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2754 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2755 EXPECT_EQ(12, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2756
2757 // Trigger adapt up, expect upscaled resolution (960x540@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002758 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002759 timestamp_ms += kFrameIntervalMs;
2760 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002761 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002762 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
2763 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2764 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2765 EXPECT_EQ(13, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2766
2767 // Trigger adapt up, expect no restriction (1280x720fps@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002768 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002769 timestamp_ms += kFrameIntervalMs;
2770 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002771 WaitForEncodedFrame(kWidth, kHeight);
asaperssonf7e294d2017-06-13 23:25:22 -07002772 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002773 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002774 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2775 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2776 EXPECT_EQ(14, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2777
2778 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07002779 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002780 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002781 EXPECT_EQ(14, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2782
mflodmancc3d4422017-08-03 08:27:51 -07002783 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07002784}
2785
mflodmancc3d4422017-08-03 08:27:51 -07002786TEST_F(VideoStreamEncoderTest, AdaptWithTwoReasonsAndDifferentOrder_Framerate) {
asaperssonf7e294d2017-06-13 23:25:22 -07002787 const int kWidth = 1280;
2788 const int kHeight = 720;
2789 const int64_t kFrameIntervalMs = 150;
2790 int64_t timestamp_ms = kFrameIntervalMs;
mflodmancc3d4422017-08-03 08:27:51 -07002791 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07002792
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002793 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07002794 AdaptingFrameForwarder source;
2795 source.set_adaptation_enabled(true);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002796 video_stream_encoder_->SetSource(&source,
2797 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07002798 timestamp_ms += kFrameIntervalMs;
2799 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002800 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002801 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002802 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2803 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2804 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2805 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2806 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2807 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2808
2809 // Trigger cpu adapt down, expect scaled down resolution (960x540@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002810 video_stream_encoder_->TriggerCpuOveruse();
asaperssonf7e294d2017-06-13 23:25:22 -07002811 timestamp_ms += kFrameIntervalMs;
2812 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002813 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002814 VerifyFpsMaxResolutionLt(source.sink_wants(), kWidth * kHeight);
2815 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2816 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2817 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
2818 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2819 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2820 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2821
2822 // Trigger cpu adapt down, expect scaled down resolution (640x360@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002823 video_stream_encoder_->TriggerCpuOveruse();
asaperssonf7e294d2017-06-13 23:25:22 -07002824 timestamp_ms += kFrameIntervalMs;
2825 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002826 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002827 VerifyFpsMaxResolutionLt(source.sink_wants(), source.last_wants());
2828 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2829 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2830 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
2831 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2832 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2833 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2834
2835 // Trigger quality adapt down, expect reduced fps (640x360@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002836 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002837 timestamp_ms += kFrameIntervalMs;
2838 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002839 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002840 VerifyFpsLtResolutionEq(source.sink_wants(), source.last_wants());
2841 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2842 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2843 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
2844 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2845 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2846 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2847
2848 // Trigger cpu adapt up, expect increased fps (640x360@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002849 video_stream_encoder_->TriggerCpuNormalUsage();
asaperssonf7e294d2017-06-13 23:25:22 -07002850 timestamp_ms += kFrameIntervalMs;
2851 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002852 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002853 VerifyFpsMaxResolutionEq(source.sink_wants(), source.last_wants());
2854 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2855 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2856 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
2857 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2858 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2859 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2860
2861 // Trigger quality adapt up, expect upscaled resolution (960x540@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002862 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002863 timestamp_ms += kFrameIntervalMs;
2864 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002865 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002866 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
2867 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2868 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2869 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_resolution);
2870 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2871 EXPECT_EQ(3, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2872 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2873
2874 // Trigger cpu adapt up, expect no restriction (1280x720fps@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002875 video_stream_encoder_->TriggerCpuNormalUsage();
asaperssonf7e294d2017-06-13 23:25:22 -07002876 timestamp_ms += kFrameIntervalMs;
2877 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002878 WaitForEncodedFrame(kWidth, kHeight);
asaperssonf7e294d2017-06-13 23:25:22 -07002879 VerifyFpsMaxResolutionGt(source.sink_wants(), source.last_wants());
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002880 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002881 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2882 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2883 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2884 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2885 EXPECT_EQ(4, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2886 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2887
2888 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07002889 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002890 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002891 EXPECT_EQ(4, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2892 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2893
mflodmancc3d4422017-08-03 08:27:51 -07002894 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07002895}
2896
mflodmancc3d4422017-08-03 08:27:51 -07002897TEST_F(VideoStreamEncoderTest,
2898 AdaptWithTwoReasonsAndDifferentOrder_Resolution) {
asaperssonf7e294d2017-06-13 23:25:22 -07002899 const int kWidth = 640;
2900 const int kHeight = 360;
2901 const int kFpsLimit = 15;
2902 const int64_t kFrameIntervalMs = 150;
2903 int64_t timestamp_ms = kFrameIntervalMs;
mflodmancc3d4422017-08-03 08:27:51 -07002904 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
asaperssonf7e294d2017-06-13 23:25:22 -07002905
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002906 // Enable BALANCED preference, no initial limitation.
asaperssonf7e294d2017-06-13 23:25:22 -07002907 AdaptingFrameForwarder source;
2908 source.set_adaptation_enabled(true);
Taylor Brandstetter49fcc102018-05-16 14:20:41 -07002909 video_stream_encoder_->SetSource(&source,
2910 webrtc::DegradationPreference::BALANCED);
asaperssonf7e294d2017-06-13 23:25:22 -07002911 timestamp_ms += kFrameIntervalMs;
2912 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002913 WaitForEncodedFrame(kWidth, kHeight);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002914 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002915 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2916 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2917 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2918 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2919 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2920 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2921
2922 // Trigger cpu adapt down, expect scaled down framerate (640x360@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002923 video_stream_encoder_->TriggerCpuOveruse();
asaperssonf7e294d2017-06-13 23:25:22 -07002924 timestamp_ms += kFrameIntervalMs;
2925 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002926 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002927 VerifyFpsEqResolutionMax(source.sink_wants(), kFpsLimit);
2928 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2929 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2930 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2931 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_framerate);
2932 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2933 EXPECT_EQ(0, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2934
2935 // Trigger quality adapt down, expect scaled down resolution (480x270@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002936 video_stream_encoder_->TriggerQualityLow();
asaperssonf7e294d2017-06-13 23:25:22 -07002937 timestamp_ms += kFrameIntervalMs;
2938 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002939 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002940 VerifyFpsEqResolutionLt(source.sink_wants(), source.last_wants());
2941 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_resolution);
2942 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2943 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2944 EXPECT_TRUE(stats_proxy_->GetStats().cpu_limited_framerate);
2945 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2946 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2947
2948 // Trigger cpu adapt up, expect upscaled resolution (640x360@15fps).
mflodmancc3d4422017-08-03 08:27:51 -07002949 video_stream_encoder_->TriggerCpuNormalUsage();
asaperssonf7e294d2017-06-13 23:25:22 -07002950 timestamp_ms += kFrameIntervalMs;
2951 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002952 WaitForEncodedFrame(timestamp_ms);
asaperssonf7e294d2017-06-13 23:25:22 -07002953 VerifyFpsEqResolutionGt(source.sink_wants(), source.last_wants());
2954 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2955 EXPECT_TRUE(stats_proxy_->GetStats().bw_limited_framerate);
2956 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2957 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2958 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2959 EXPECT_EQ(1, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2960
2961 // Trigger quality adapt up, expect increased fps (640x360@30fps).
mflodmancc3d4422017-08-03 08:27:51 -07002962 video_stream_encoder_->TriggerQualityHigh();
asaperssonf7e294d2017-06-13 23:25:22 -07002963 timestamp_ms += kFrameIntervalMs;
2964 source.IncomingCapturedFrame(CreateFrame(timestamp_ms, kWidth, kHeight));
sprang4847ae62017-06-27 07:06:52 -07002965 WaitForEncodedFrame(timestamp_ms);
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002966 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002967 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_resolution);
2968 EXPECT_FALSE(stats_proxy_->GetStats().bw_limited_framerate);
2969 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_resolution);
2970 EXPECT_FALSE(stats_proxy_->GetStats().cpu_limited_framerate);
2971 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2972 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2973
2974 // Trigger adapt up, expect no change.
mflodmancc3d4422017-08-03 08:27:51 -07002975 video_stream_encoder_->TriggerQualityHigh();
Mirko Bonadei948b7e32018-08-14 07:23:21 +00002976 VerifyNoLimitation(source.sink_wants());
asaperssonf7e294d2017-06-13 23:25:22 -07002977 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_cpu_adapt_changes);
2978 EXPECT_EQ(2, stats_proxy_->GetStats().number_of_quality_adapt_changes);
2979
mflodmancc3d4422017-08-03 08:27:51 -07002980 video_stream_encoder_->Stop();
asaperssonf7e294d2017-06-13 23:25:22 -07002981}
2982
mflodmancc3d4422017-08-03 08:27:51 -07002983TEST_F(VideoStreamEncoderTest, AcceptsFullHdAdaptedDownSimulcastFrames) {
ilnik6b826ef2017-06-16 06:53:48 -07002984 // Simulates simulcast behavior and makes highest stream resolutions divisible
2985 // by 4.
2986 class CroppingVideoStreamFactory
2987 : public VideoEncoderConfig::VideoStreamFactoryInterface {
2988 public:
2989 explicit CroppingVideoStreamFactory(size_t num_temporal_layers,
2990 int framerate)
2991 : num_temporal_layers_(num_temporal_layers), framerate_(framerate) {
2992 EXPECT_GT(num_temporal_layers, 0u);
2993 EXPECT_GT(framerate, 0);
2994 }
2995
2996 private:
2997 std::vector<VideoStream> CreateEncoderStreams(
2998 int width,
2999 int height,
3000 const VideoEncoderConfig& encoder_config) override {
Yves Gerey665174f2018-06-19 15:03:05 +02003001 std::vector<VideoStream> streams = test::CreateVideoStreams(
3002 width - width % 4, height - height % 4, encoder_config);
ilnik6b826ef2017-06-16 06:53:48 -07003003 for (VideoStream& stream : streams) {
Sergey Silkina796a7e2018-03-01 15:11:29 +01003004 stream.num_temporal_layers = num_temporal_layers_;
ilnik6b826ef2017-06-16 06:53:48 -07003005 stream.max_framerate = framerate_;
3006 }
3007 return streams;
3008 }
3009
3010 const size_t num_temporal_layers_;
3011 const int framerate_;
3012 };
3013
3014 const int kFrameWidth = 1920;
3015 const int kFrameHeight = 1080;
3016 // 3/4 of 1920.
3017 const int kAdaptedFrameWidth = 1440;
3018 // 3/4 of 1080 rounded down to multiple of 4.
3019 const int kAdaptedFrameHeight = 808;
3020 const int kFramerate = 24;
3021
mflodmancc3d4422017-08-03 08:27:51 -07003022 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
ilnik6b826ef2017-06-16 06:53:48 -07003023 // Trigger reconfigure encoder (without resetting the entire instance).
3024 VideoEncoderConfig video_encoder_config;
Niels Möller259a4972018-04-05 15:36:51 +02003025 video_encoder_config.codec_type = kVideoCodecVP8;
ilnik6b826ef2017-06-16 06:53:48 -07003026 video_encoder_config.max_bitrate_bps = kTargetBitrateBps;
3027 video_encoder_config.number_of_streams = 1;
3028 video_encoder_config.video_stream_factory =
3029 new rtc::RefCountedObject<CroppingVideoStreamFactory>(1, kFramerate);
mflodmancc3d4422017-08-03 08:27:51 -07003030 video_stream_encoder_->ConfigureEncoder(std::move(video_encoder_config),
Niels Möllerf1338562018-04-26 09:51:47 +02003031 kMaxPayloadLength);
mflodmancc3d4422017-08-03 08:27:51 -07003032 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
ilnik6b826ef2017-06-16 06:53:48 -07003033
3034 video_source_.set_adaptation_enabled(true);
3035
3036 video_source_.IncomingCapturedFrame(
3037 CreateFrame(1, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07003038 WaitForEncodedFrame(kFrameWidth, kFrameHeight);
ilnik6b826ef2017-06-16 06:53:48 -07003039
3040 // Trigger CPU overuse, downscale by 3/4.
mflodmancc3d4422017-08-03 08:27:51 -07003041 video_stream_encoder_->TriggerCpuOveruse();
ilnik6b826ef2017-06-16 06:53:48 -07003042 video_source_.IncomingCapturedFrame(
3043 CreateFrame(2, kFrameWidth, kFrameHeight));
sprang4847ae62017-06-27 07:06:52 -07003044 WaitForEncodedFrame(kAdaptedFrameWidth, kAdaptedFrameHeight);
ilnik6b826ef2017-06-16 06:53:48 -07003045
mflodmancc3d4422017-08-03 08:27:51 -07003046 video_stream_encoder_->Stop();
ilnik6b826ef2017-06-16 06:53:48 -07003047}
3048
mflodmancc3d4422017-08-03 08:27:51 -07003049TEST_F(VideoStreamEncoderTest, PeriodicallyUpdatesChannelParameters) {
sprang4847ae62017-06-27 07:06:52 -07003050 const int kFrameWidth = 1280;
3051 const int kFrameHeight = 720;
3052 const int kLowFps = 2;
3053 const int kHighFps = 30;
3054
mflodmancc3d4422017-08-03 08:27:51 -07003055 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
sprang4847ae62017-06-27 07:06:52 -07003056
3057 int64_t timestamp_ms = fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec;
3058 max_framerate_ = kLowFps;
3059
3060 // Insert 2 seconds of 2fps video.
3061 for (int i = 0; i < kLowFps * 2; ++i) {
3062 video_source_.IncomingCapturedFrame(
3063 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
3064 WaitForEncodedFrame(timestamp_ms);
3065 timestamp_ms += 1000 / kLowFps;
3066 }
3067
3068 // Make sure encoder is updated with new target.
mflodmancc3d4422017-08-03 08:27:51 -07003069 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
sprang4847ae62017-06-27 07:06:52 -07003070 video_source_.IncomingCapturedFrame(
3071 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
3072 WaitForEncodedFrame(timestamp_ms);
3073 timestamp_ms += 1000 / kLowFps;
3074
3075 EXPECT_EQ(kLowFps, fake_encoder_.GetConfiguredInputFramerate());
3076
3077 // Insert 30fps frames for just a little more than the forced update period.
3078 const int kVcmTimerIntervalFrames =
3079 (vcm::VCMProcessTimer::kDefaultProcessIntervalMs * kHighFps) / 1000;
3080 const int kFrameIntervalMs = 1000 / kHighFps;
3081 max_framerate_ = kHighFps;
3082 for (int i = 0; i < kVcmTimerIntervalFrames + 2; ++i) {
3083 video_source_.IncomingCapturedFrame(
3084 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
3085 // Wait for encoded frame, but skip ahead if it doesn't arrive as it might
3086 // be dropped if the encoder hans't been updated with the new higher target
3087 // framerate yet, causing it to overshoot the target bitrate and then
3088 // suffering the wrath of the media optimizer.
3089 TimedWaitForEncodedFrame(timestamp_ms, 2 * kFrameIntervalMs);
3090 timestamp_ms += kFrameIntervalMs;
3091 }
3092
3093 // Don expect correct measurement just yet, but it should be higher than
3094 // before.
3095 EXPECT_GT(fake_encoder_.GetConfiguredInputFramerate(), kLowFps);
3096
mflodmancc3d4422017-08-03 08:27:51 -07003097 video_stream_encoder_->Stop();
sprang4847ae62017-06-27 07:06:52 -07003098}
3099
mflodmancc3d4422017-08-03 08:27:51 -07003100TEST_F(VideoStreamEncoderTest, DoesNotUpdateBitrateAllocationWhenSuspended) {
sprang4847ae62017-06-27 07:06:52 -07003101 const int kFrameWidth = 1280;
3102 const int kFrameHeight = 720;
3103 const int kTargetBitrateBps = 1000000;
3104
3105 MockBitrateObserver bitrate_observer;
Niels Möller0327c2d2018-05-21 14:09:31 +02003106 video_stream_encoder_->SetBitrateAllocationObserver(&bitrate_observer);
sprang4847ae62017-06-27 07:06:52 -07003107
3108 EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(_)).Times(1);
3109 // Initial bitrate update.
mflodmancc3d4422017-08-03 08:27:51 -07003110 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
3111 video_stream_encoder_->WaitUntilTaskQueueIsIdle();
sprang4847ae62017-06-27 07:06:52 -07003112
3113 // Insert a first video frame, causes another bitrate update.
3114 int64_t timestamp_ms = fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec;
3115 EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(_)).Times(1);
3116 video_source_.IncomingCapturedFrame(
3117 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
3118 WaitForEncodedFrame(timestamp_ms);
3119
3120 // Next, simulate video suspension due to pacer queue overrun.
mflodmancc3d4422017-08-03 08:27:51 -07003121 video_stream_encoder_->OnBitrateUpdated(0, 0, 1);
sprang4847ae62017-06-27 07:06:52 -07003122
3123 // Skip ahead until a new periodic parameter update should have occured.
3124 timestamp_ms += vcm::VCMProcessTimer::kDefaultProcessIntervalMs;
3125 fake_clock_.AdvanceTimeMicros(
3126 vcm::VCMProcessTimer::kDefaultProcessIntervalMs *
3127 rtc::kNumMicrosecsPerMillisec);
3128
3129 // Bitrate observer should not be called.
3130 EXPECT_CALL(bitrate_observer, OnBitrateAllocationUpdated(_)).Times(0);
3131 video_source_.IncomingCapturedFrame(
3132 CreateFrame(timestamp_ms, kFrameWidth, kFrameHeight));
3133 ExpectDroppedFrame();
3134
mflodmancc3d4422017-08-03 08:27:51 -07003135 video_stream_encoder_->Stop();
sprang4847ae62017-06-27 07:06:52 -07003136}
ilnik6b826ef2017-06-16 06:53:48 -07003137
Niels Möller4db138e2018-04-19 09:04:13 +02003138TEST_F(VideoStreamEncoderTest,
3139 DefaultCpuAdaptationThresholdsForSoftwareEncoder) {
3140 const int kFrameWidth = 1280;
3141 const int kFrameHeight = 720;
3142 const CpuOveruseOptions default_options;
3143 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
3144 video_source_.IncomingCapturedFrame(
3145 CreateFrame(1, kFrameWidth, kFrameHeight));
3146 WaitForEncodedFrame(1);
3147 EXPECT_EQ(video_stream_encoder_->overuse_detector_proxy_->GetOptions()
3148 .low_encode_usage_threshold_percent,
3149 default_options.low_encode_usage_threshold_percent);
3150 EXPECT_EQ(video_stream_encoder_->overuse_detector_proxy_->GetOptions()
3151 .high_encode_usage_threshold_percent,
3152 default_options.high_encode_usage_threshold_percent);
3153 video_stream_encoder_->Stop();
3154}
3155
3156TEST_F(VideoStreamEncoderTest,
3157 HigherCpuAdaptationThresholdsForHardwareEncoder) {
3158 const int kFrameWidth = 1280;
3159 const int kFrameHeight = 720;
3160 CpuOveruseOptions hardware_options;
3161 hardware_options.low_encode_usage_threshold_percent = 150;
3162 hardware_options.high_encode_usage_threshold_percent = 200;
3163 encoder_factory_.SetIsHardwareAccelerated(true);
3164
3165 video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
3166 video_source_.IncomingCapturedFrame(
3167 CreateFrame(1, kFrameWidth, kFrameHeight));
3168 WaitForEncodedFrame(1);
3169 EXPECT_EQ(video_stream_encoder_->overuse_detector_proxy_->GetOptions()
3170 .low_encode_usage_threshold_percent,
3171 hardware_options.low_encode_usage_threshold_percent);
3172 EXPECT_EQ(video_stream_encoder_->overuse_detector_proxy_->GetOptions()
3173 .high_encode_usage_threshold_percent,
3174 hardware_options.high_encode_usage_threshold_percent);
3175 video_stream_encoder_->Stop();
3176}
3177
perkj26091b12016-09-01 01:17:40 -07003178} // namespace webrtc