blob: ea082a5e6622bd5914ce301b683131b1cfff6f6f [file] [log] [blame]
Henrik Boströmb08882b2020-01-07 09:11:171/*
2 * Copyright 2019 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
11#include "video/overuse_frame_detector_resource_adaptation_module.h"
12
13#include <algorithm>
14#include <limits>
15#include <memory>
16#include <string>
17#include <utility>
18
19#include "absl/algorithm/container.h"
Danil Chapovalov64f1f3f2020-01-16 13:41:1020#include "absl/base/macros.h"
Henrik Boström07b17df2020-01-15 10:42:1221#include "api/task_queue/task_queue_base.h"
Henrik Boströmb08882b2020-01-07 09:11:1722#include "api/video/video_source_interface.h"
Henrik Boströmce0ea492020-01-13 10:27:1823#include "call/adaptation/video_source_restrictions.h"
Henrik Boströmb08882b2020-01-07 09:11:1724#include "rtc_base/logging.h"
Henrik Boströmd2382002020-01-10 14:44:0125#include "rtc_base/numerics/safe_conversions.h"
Henrik Boströmb08882b2020-01-07 09:11:1726#include "rtc_base/strings/string_builder.h"
Henrik Boströmb08882b2020-01-07 09:11:1727#include "video/video_stream_encoder.h"
28
29namespace webrtc {
30
31namespace {
32
33const int kMinFramerateFps = 2;
34
35bool IsResolutionScalingEnabled(DegradationPreference degradation_preference) {
36 return degradation_preference == DegradationPreference::MAINTAIN_FRAMERATE ||
37 degradation_preference == DegradationPreference::BALANCED;
38}
39
40bool IsFramerateScalingEnabled(DegradationPreference degradation_preference) {
41 return degradation_preference == DegradationPreference::MAINTAIN_RESOLUTION ||
42 degradation_preference == DegradationPreference::BALANCED;
43}
44
Henrik Boström8234b922020-01-13 16:26:5045// Returns modified restrictions where any constraints that don't apply to the
46// degradation preference are cleared.
47VideoSourceRestrictions ApplyDegradationPreference(
48 VideoSourceRestrictions source_restrictions,
49 DegradationPreference degradation_preference) {
50 switch (degradation_preference) {
51 case DegradationPreference::BALANCED:
52 break;
53 case DegradationPreference::MAINTAIN_FRAMERATE:
54 source_restrictions.set_max_frame_rate(absl::nullopt);
55 break;
56 case DegradationPreference::MAINTAIN_RESOLUTION:
57 source_restrictions.set_max_pixels_per_frame(absl::nullopt);
58 source_restrictions.set_target_pixels_per_frame(absl::nullopt);
59 break;
60 case DegradationPreference::DISABLED:
61 source_restrictions.set_max_pixels_per_frame(absl::nullopt);
62 source_restrictions.set_target_pixels_per_frame(absl::nullopt);
63 source_restrictions.set_max_frame_rate(absl::nullopt);
64 }
65 return source_restrictions;
66}
67
Henrik Boströmb08882b2020-01-07 09:11:1768} // namespace
69
Henrik Boströmce0ea492020-01-13 10:27:1870// VideoSourceRestrictor is responsible for keeping track of current
71// VideoSourceRestrictions and how to modify them in response to adapting up or
72// down. It is not reponsible for determining when we should adapt up or down -
73// for that, see OveruseFrameDetectorResourceAdaptationModule::AdaptUp() and
74// AdaptDown() - only how to modify the source/sink restrictions when this
75// happens. Note that it is also not responsible for reconfigruring the
76// source/sink, it is only a keeper of desired restrictions.
Henrik Boströmce0ea492020-01-13 10:27:1877class OveruseFrameDetectorResourceAdaptationModule::VideoSourceRestrictor {
Henrik Boströmb08882b2020-01-07 09:11:1778 public:
Henrik Boströma3d42522020-01-16 12:55:2979 VideoSourceRestrictor() {}
Henrik Boströmb08882b2020-01-07 09:11:1780
Henrik Boströmce0ea492020-01-13 10:27:1881 VideoSourceRestrictions source_restrictions() {
Henrik Boströmce0ea492020-01-13 10:27:1882 return source_restrictions_;
Henrik Boströmd2382002020-01-10 14:44:0183 }
84
Henrik Boström07b17df2020-01-15 10:42:1285 // Updates the source_restrictions(). The source/sink has to be informed of
86 // this separately.
Henrik Boström8234b922020-01-13 16:26:5087 void ClearRestrictions() {
Henrik Boströmce0ea492020-01-13 10:27:1888 source_restrictions_ = VideoSourceRestrictions();
Henrik Boströmb08882b2020-01-07 09:11:1789 }
90
Henrik Boströmce0ea492020-01-13 10:27:1891 // Updates the source_restrictions(). The source/sink has to be informed of
92 // this separately.
Henrik Boströmb08882b2020-01-07 09:11:1793 bool RequestResolutionLowerThan(int pixel_count,
94 int min_pixels_per_frame,
95 bool* min_pixels_reached) {
Henrik Boströmb08882b2020-01-07 09:11:1796 // The input video frame size will have a resolution less than or equal to
97 // |max_pixel_count| depending on how the source can scale the frame size.
98 const int pixels_wanted = (pixel_count * 3) / 5;
Henrik Boströmce0ea492020-01-13 10:27:1899 if (pixels_wanted >=
100 rtc::dchecked_cast<int>(
101 source_restrictions_.max_pixels_per_frame().value_or(
102 std::numeric_limits<int>::max()))) {
Henrik Boströmb08882b2020-01-07 09:11:17103 return false;
104 }
105 if (pixels_wanted < min_pixels_per_frame) {
106 *min_pixels_reached = true;
107 return false;
108 }
109 RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: "
110 << pixels_wanted;
Henrik Boströmce0ea492020-01-13 10:27:18111 source_restrictions_.set_max_pixels_per_frame(
112 pixels_wanted != std::numeric_limits<int>::max()
113 ? absl::optional<size_t>(pixels_wanted)
114 : absl::nullopt);
115 source_restrictions_.set_target_pixels_per_frame(absl::nullopt);
Henrik Boströmb08882b2020-01-07 09:11:17116 return true;
117 }
118
Henrik Boströmce0ea492020-01-13 10:27:18119 // Updates the source_restrictions(). The source/sink has to be informed of
120 // this separately.
Henrik Boströmb08882b2020-01-07 09:11:17121 int RequestFramerateLowerThan(int fps) {
Henrik Boströmb08882b2020-01-07 09:11:17122 // The input video frame rate will be scaled down to 2/3, rounding down.
123 int framerate_wanted = (fps * 2) / 3;
124 return RestrictFramerate(framerate_wanted) ? framerate_wanted : -1;
125 }
126
127 int GetHigherResolutionThan(int pixel_count) const {
128 // On step down we request at most 3/5 the pixel count of the previous
129 // resolution, so in order to take "one step up" we request a resolution
130 // as close as possible to 5/3 of the current resolution. The actual pixel
131 // count selected depends on the capabilities of the source. In order to
132 // not take a too large step up, we cap the requested pixel count to be at
133 // most four time the current number of pixels.
134 return (pixel_count * 5) / 3;
135 }
136
Henrik Boströmce0ea492020-01-13 10:27:18137 // Updates the source_restrictions(). The source/sink has to be informed of
138 // this separately.
Henrik Boströmb08882b2020-01-07 09:11:17139 bool RequestHigherResolutionThan(int pixel_count) {
Henrik Boströmb08882b2020-01-07 09:11:17140 int max_pixels_wanted = pixel_count;
141 if (max_pixels_wanted != std::numeric_limits<int>::max())
142 max_pixels_wanted = pixel_count * 4;
143
Henrik Boströmce0ea492020-01-13 10:27:18144 if (max_pixels_wanted <=
145 rtc::dchecked_cast<int>(
146 source_restrictions_.max_pixels_per_frame().value_or(
147 std::numeric_limits<int>::max()))) {
Henrik Boströmb08882b2020-01-07 09:11:17148 return false;
Henrik Boströmb08882b2020-01-07 09:11:17149 }
Henrik Boströmce0ea492020-01-13 10:27:18150
Henrik Boströmb08882b2020-01-07 09:11:17151 RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
152 << max_pixels_wanted;
Henrik Boströmce0ea492020-01-13 10:27:18153 source_restrictions_.set_max_pixels_per_frame(
154 max_pixels_wanted != std::numeric_limits<int>::max()
155 ? absl::optional<size_t>(max_pixels_wanted)
156 : absl::nullopt);
157 source_restrictions_.set_target_pixels_per_frame(
158 max_pixels_wanted != std::numeric_limits<int>::max()
159 ? absl::optional<size_t>(GetHigherResolutionThan(pixel_count))
160 : absl::nullopt);
Henrik Boströmb08882b2020-01-07 09:11:17161 return true;
162 }
163
Henrik Boströmce0ea492020-01-13 10:27:18164 // Updates the source_restrictions(). The source/sink has to be informed of
165 // this separately.
Henrik Boströmb08882b2020-01-07 09:11:17166 // Request upgrade in framerate. Returns the new requested frame, or -1 if
167 // no change requested. Note that maxint may be returned if limits due to
168 // adaptation requests are removed completely. In that case, consider
169 // |max_framerate_| to be the current limit (assuming the capturer complies).
170 int RequestHigherFramerateThan(int fps) {
Henrik Boströmb08882b2020-01-07 09:11:17171 // The input frame rate will be scaled up to the last step, with rounding.
172 int framerate_wanted = fps;
173 if (fps != std::numeric_limits<int>::max())
174 framerate_wanted = (fps * 3) / 2;
175
176 return IncreaseFramerate(framerate_wanted) ? framerate_wanted : -1;
177 }
178
Henrik Boströmce0ea492020-01-13 10:27:18179 // Updates the source_restrictions(). The source/sink has to be informed of
180 // this separately.
Henrik Boströmb08882b2020-01-07 09:11:17181 bool RestrictFramerate(int fps) {
Henrik Boströmb08882b2020-01-07 09:11:17182 const int fps_wanted = std::max(kMinFramerateFps, fps);
Henrik Boströmce0ea492020-01-13 10:27:18183 if (fps_wanted >=
184 rtc::dchecked_cast<int>(source_restrictions_.max_frame_rate().value_or(
185 std::numeric_limits<int>::max())))
Henrik Boströmb08882b2020-01-07 09:11:17186 return false;
187
188 RTC_LOG(LS_INFO) << "Scaling down framerate: " << fps_wanted;
Henrik Boströmce0ea492020-01-13 10:27:18189 source_restrictions_.set_max_frame_rate(
190 fps_wanted != std::numeric_limits<int>::max()
191 ? absl::optional<double>(fps_wanted)
192 : absl::nullopt);
Henrik Boströmb08882b2020-01-07 09:11:17193 return true;
194 }
195
Henrik Boströmce0ea492020-01-13 10:27:18196 // Updates the source_restrictions(). The source/sink has to be informed of
197 // this separately.
Henrik Boströmb08882b2020-01-07 09:11:17198 bool IncreaseFramerate(int fps) {
Henrik Boströmb08882b2020-01-07 09:11:17199 const int fps_wanted = std::max(kMinFramerateFps, fps);
Henrik Boströmce0ea492020-01-13 10:27:18200 if (fps_wanted <=
201 rtc::dchecked_cast<int>(source_restrictions_.max_frame_rate().value_or(
202 std::numeric_limits<int>::max())))
Henrik Boströmb08882b2020-01-07 09:11:17203 return false;
204
205 RTC_LOG(LS_INFO) << "Scaling up framerate: " << fps_wanted;
Henrik Boströmce0ea492020-01-13 10:27:18206 source_restrictions_.set_max_frame_rate(
207 fps_wanted != std::numeric_limits<int>::max()
208 ? absl::optional<double>(fps_wanted)
209 : absl::nullopt);
Henrik Boströmb08882b2020-01-07 09:11:17210 return true;
211 }
212
213 private:
Henrik Boström07b17df2020-01-15 10:42:12214 VideoSourceRestrictions source_restrictions_;
Henrik Boströmb08882b2020-01-07 09:11:17215
Henrik Boströmce0ea492020-01-13 10:27:18216 RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceRestrictor);
Henrik Boströmb08882b2020-01-07 09:11:17217};
218
219// Class holding adaptation information.
220OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::AdaptCounter() {
221 fps_counters_.resize(kScaleReasonSize);
222 resolution_counters_.resize(kScaleReasonSize);
223 static_assert(kScaleReasonSize == 2, "Update MoveCount.");
224}
225
226OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::~AdaptCounter() {}
227
228std::string
229OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString() const {
230 rtc::StringBuilder ss;
231 ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
232 ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
233 return ss.Release();
234}
235
236VideoStreamEncoderObserver::AdaptationSteps
237OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Counts(
238 int reason) const {
239 VideoStreamEncoderObserver::AdaptationSteps counts;
240 counts.num_framerate_reductions = fps_counters_[reason];
241 counts.num_resolution_reductions = resolution_counters_[reason];
242 return counts;
243}
244
245void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
246 IncrementFramerate(int reason) {
247 ++(fps_counters_[reason]);
248}
249
250void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
251 IncrementResolution(int reason) {
252 ++(resolution_counters_[reason]);
253}
254
255void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
256 DecrementFramerate(int reason) {
257 if (fps_counters_[reason] == 0) {
258 // Balanced mode: Adapt up is in a different order, switch reason.
259 // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
260 // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
261 // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
262 // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
263 // 4. Up resolution (quality): res={quality:0,cpu:0}, fps={quality:0,cpu:0}
264 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
265 RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
266 MoveCount(&resolution_counters_, reason);
267 MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize);
268 }
269 --(fps_counters_[reason]);
270 RTC_DCHECK_GE(fps_counters_[reason], 0);
271}
272
273void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
274 DecrementResolution(int reason) {
275 if (resolution_counters_[reason] == 0) {
276 // Balanced mode: Adapt up is in a different order, switch reason.
277 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
278 RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
279 MoveCount(&fps_counters_, reason);
280 MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize);
281 }
282 --(resolution_counters_[reason]);
283 RTC_DCHECK_GE(resolution_counters_[reason], 0);
284}
285
286void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
287 DecrementFramerate(int reason, int cur_fps) {
288 DecrementFramerate(reason);
289 // Reset if at max fps (i.e. in case of fewer steps up than down).
290 if (cur_fps == std::numeric_limits<int>::max())
291 absl::c_fill(fps_counters_, 0);
292}
293
294int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount()
295 const {
296 return Count(fps_counters_);
297}
298
299int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
300 ResolutionCount() const {
301 return Count(resolution_counters_);
302}
303
304int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount(
305 int reason) const {
306 return fps_counters_[reason];
307}
308
309int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ResolutionCount(
310 int reason) const {
311 return resolution_counters_[reason];
312}
313
314int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::TotalCount(
315 int reason) const {
316 return FramerateCount(reason) + ResolutionCount(reason);
317}
318
319int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Count(
320 const std::vector<int>& counters) const {
321 return absl::c_accumulate(counters, 0);
322}
323
324void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::MoveCount(
325 std::vector<int>* counters,
326 int from_reason) {
327 int to_reason = (from_reason + 1) % kScaleReasonSize;
328 ++((*counters)[to_reason]);
329 --((*counters)[from_reason]);
330}
331
332std::string
333OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString(
334 const std::vector<int>& counters) const {
335 rtc::StringBuilder ss;
336 for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
337 ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
338 }
339 return ss.Release();
340}
341
342OveruseFrameDetectorResourceAdaptationModule::
343 OveruseFrameDetectorResourceAdaptationModule(
Henrik Boström382cc6d2020-01-07 09:15:04344 VideoStreamEncoder* video_stream_encoder,
Henrik Boströmb08882b2020-01-07 09:11:17345 std::unique_ptr<OveruseFrameDetector> overuse_detector,
Henrik Boströmd2382002020-01-10 14:44:01346 VideoStreamEncoderObserver* encoder_stats_observer,
347 ResourceAdaptationModuleListener* adaptation_listener)
Henrik Boström07b17df2020-01-15 10:42:12348 : adaptation_listener_(adaptation_listener),
Henrik Boström382cc6d2020-01-07 09:15:04349 video_stream_encoder_(video_stream_encoder),
Henrik Boströma3d42522020-01-16 12:55:29350 has_input_video_(false),
Henrik Boströmb08882b2020-01-07 09:11:17351 degradation_preference_(DegradationPreference::DISABLED),
352 adapt_counters_(),
353 balanced_settings_(),
354 last_adaptation_request_(absl::nullopt),
355 last_frame_pixel_count_(absl::nullopt),
Henrik Boström8234b922020-01-13 16:26:50356 source_restrictor_(std::make_unique<VideoSourceRestrictor>()),
Henrik Boströmb08882b2020-01-07 09:11:17357 overuse_detector_(std::move(overuse_detector)),
Henrik Boströmfae6f0e2020-01-20 10:16:50358 overuse_detector_is_started_(false),
359 codec_max_frame_rate_(absl::nullopt),
360 target_frame_rate_(absl::nullopt),
Henrik Boströmb08882b2020-01-07 09:11:17361 encoder_start_bitrate_bps_(0),
362 is_quality_scaler_enabled_(false),
363 encoder_config_(),
364 encoder_(nullptr),
365 encoder_stats_observer_(encoder_stats_observer) {
Henrik Boströmd2382002020-01-10 14:44:01366 RTC_DCHECK(adaptation_listener_);
Henrik Boström382cc6d2020-01-07 09:15:04367 RTC_DCHECK(video_stream_encoder_);
Henrik Boströmb08882b2020-01-07 09:11:17368 RTC_DCHECK(overuse_detector_);
369 RTC_DCHECK(encoder_stats_observer_);
370}
371
372OveruseFrameDetectorResourceAdaptationModule::
373 ~OveruseFrameDetectorResourceAdaptationModule() {}
374
Henrik Boströmb08882b2020-01-07 09:11:17375void OveruseFrameDetectorResourceAdaptationModule::SetEncoder(
376 VideoEncoder* encoder) {
Henrik Boströmb08882b2020-01-07 09:11:17377 encoder_ = encoder;
378}
379
Henrik Boströma3d42522020-01-16 12:55:29380void OveruseFrameDetectorResourceAdaptationModule::StartResourceAdaptation(
Henrik Boströmd2382002020-01-10 14:44:01381 ResourceAdaptationModuleListener* adaptation_listener) {
Henrik Boströmb08882b2020-01-07 09:11:17382 RTC_DCHECK(encoder_);
Henrik Boströmfae6f0e2020-01-20 10:16:50383 RTC_DCHECK(!overuse_detector_is_started_);
Henrik Boströmd2382002020-01-10 14:44:01384 // TODO(hbos): When AdaptUp() and AdaptDown() are no longer invoked outside
385 // the interval between StartCheckForOveruse() and StopCheckForOveruse(),
386 // support configuring which |adaptation_listener_| to use on the fly. It is
387 // currently hardcoded for the entire lifetime of the module in order to
388 // support adaptation caused by VideoStreamEncoder or QualityScaler invoking
389 // AdaptUp() and AdaptDown() even when the OveruseDetector is inactive.
390 RTC_DCHECK_EQ(adaptation_listener, adaptation_listener_);
Henrik Boström382cc6d2020-01-07 09:15:04391 overuse_detector_->StartCheckForOveruse(
Henrik Boström07b17df2020-01-15 10:42:12392 TaskQueueBase::Current(), video_stream_encoder_->GetCpuOveruseOptions(),
393 this);
Henrik Boströmfae6f0e2020-01-20 10:16:50394 overuse_detector_is_started_ = true;
395 overuse_detector_->OnTargetFramerateUpdated(
396 target_frame_rate_.has_value()
397 ? static_cast<int>(target_frame_rate_.value())
398 : std::numeric_limits<int>::max());
Henrik Boströmb08882b2020-01-07 09:11:17399}
400
Henrik Boströma3d42522020-01-16 12:55:29401void OveruseFrameDetectorResourceAdaptationModule::StopResourceAdaptation() {
Henrik Boströmb08882b2020-01-07 09:11:17402 overuse_detector_->StopCheckForOveruse();
Henrik Boströmfae6f0e2020-01-20 10:16:50403 overuse_detector_is_started_ = false;
Henrik Boströmb08882b2020-01-07 09:11:17404}
405
Henrik Boströma3d42522020-01-16 12:55:29406void OveruseFrameDetectorResourceAdaptationModule::SetHasInputVideo(
407 bool has_input_video) {
408 // While false, AdaptUp() and AdaptDown() are NO-OPS.
409 has_input_video_ = has_input_video;
410}
411
412void OveruseFrameDetectorResourceAdaptationModule::SetDegradationPreference(
413 DegradationPreference degradation_preference) {
414 if (degradation_preference_ != degradation_preference) {
415 // Reset adaptation state, so that we're not tricked into thinking there's
416 // an already pending request of the same type.
417 last_adaptation_request_.reset();
418 if (degradation_preference == DegradationPreference::BALANCED ||
419 degradation_preference_ == DegradationPreference::BALANCED) {
420 // TODO(asapersson): Consider removing |adapt_counters_| map and use one
421 // AdaptCounter for all modes.
422 source_restrictor_->ClearRestrictions();
423 adapt_counters_.clear();
424 }
425 }
426 degradation_preference_ = degradation_preference;
427 MaybeUpdateVideoSourceRestrictions();
428}
429
Henrik Boströmfae6f0e2020-01-20 10:16:50430void OveruseFrameDetectorResourceAdaptationModule::
431 ResetVideoSourceRestrictions() {
432 last_adaptation_request_.reset();
433 source_restrictor_->ClearRestrictions();
434 adapt_counters_.clear();
435 MaybeUpdateVideoSourceRestrictions();
436}
437
Henrik Boströmb08882b2020-01-07 09:11:17438void OveruseFrameDetectorResourceAdaptationModule::FrameCaptured(
439 const VideoFrame& frame,
440 int64_t time_when_first_seen_us) {
Henrik Boströmb08882b2020-01-07 09:11:17441 overuse_detector_->FrameCaptured(frame, time_when_first_seen_us);
442}
443
444void OveruseFrameDetectorResourceAdaptationModule::FrameSent(
445 uint32_t timestamp,
446 int64_t time_sent_in_us,
447 int64_t capture_time_us,
448 absl::optional<int> encode_duration_us) {
Henrik Boströmb08882b2020-01-07 09:11:17449 overuse_detector_->FrameSent(timestamp, time_sent_in_us, capture_time_us,
450 encode_duration_us);
451}
452
453void OveruseFrameDetectorResourceAdaptationModule::SetLastFramePixelCount(
454 absl::optional<int> last_frame_pixel_count) {
Henrik Boströmb08882b2020-01-07 09:11:17455 last_frame_pixel_count_ = last_frame_pixel_count;
456}
457
458void OveruseFrameDetectorResourceAdaptationModule::SetEncoderConfig(
459 VideoEncoderConfig encoder_config) {
Henrik Boströmb08882b2020-01-07 09:11:17460 encoder_config_ = std::move(encoder_config);
461}
462
Henrik Boströmfae6f0e2020-01-20 10:16:50463void OveruseFrameDetectorResourceAdaptationModule::SetCodecMaxFrameRate(
464 absl::optional<double> codec_max_frame_rate) {
465 RTC_DCHECK(!codec_max_frame_rate.has_value() ||
466 codec_max_frame_rate.value() > 0.0);
467 codec_max_frame_rate_ = codec_max_frame_rate;
468 MaybeUpdateTargetFrameRate();
Henrik Boströmb08882b2020-01-07 09:11:17469}
470
471void OveruseFrameDetectorResourceAdaptationModule::SetEncoderStartBitrateBps(
472 uint32_t encoder_start_bitrate_bps) {
Henrik Boströmb08882b2020-01-07 09:11:17473 encoder_start_bitrate_bps_ = encoder_start_bitrate_bps;
474}
475
476void OveruseFrameDetectorResourceAdaptationModule::SetIsQualityScalerEnabled(
477 bool is_quality_scaler_enabled) {
Henrik Boströmb08882b2020-01-07 09:11:17478 is_quality_scaler_enabled_ = is_quality_scaler_enabled;
479}
480
Henrik Boströmb08882b2020-01-07 09:11:17481void OveruseFrameDetectorResourceAdaptationModule::AdaptUp(AdaptReason reason) {
Henrik Boströma3d42522020-01-16 12:55:29482 if (!has_input_video_)
483 return;
Henrik Boströmb08882b2020-01-07 09:11:17484 const AdaptCounter& adapt_counter = GetConstAdaptCounter();
485 int num_downgrades = adapt_counter.TotalCount(reason);
486 if (num_downgrades == 0)
487 return;
488 RTC_DCHECK_GT(num_downgrades, 0);
489
490 AdaptationRequest adaptation_request = {
491 *last_frame_pixel_count_, encoder_stats_observer_->GetInputFrameRate(),
492 AdaptationRequest::Mode::kAdaptUp};
493
494 bool adapt_up_requested =
495 last_adaptation_request_ &&
496 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
497
498 if (EffectiveDegradataionPreference() ==
499 DegradationPreference::MAINTAIN_FRAMERATE) {
500 if (adapt_up_requested &&
501 adaptation_request.input_pixel_count_ <=
502 last_adaptation_request_->input_pixel_count_) {
503 // Don't request higher resolution if the current resolution is not
504 // higher than the last time we asked for the resolution to be higher.
505 return;
506 }
507 }
508
509 switch (EffectiveDegradataionPreference()) {
510 case DegradationPreference::BALANCED: {
511 // Check if quality should be increased based on bitrate.
512 if (reason == kQuality &&
Åsa Perssonf5e71e42020-01-09 09:04:53513 !balanced_settings_.CanAdaptUp(encoder_config_.codec_type,
514 *last_frame_pixel_count_,
Henrik Boströmb08882b2020-01-07 09:11:17515 encoder_start_bitrate_bps_)) {
516 return;
517 }
518 // Try scale up framerate, if higher.
519 int fps = balanced_settings_.MaxFps(encoder_config_.codec_type,
520 *last_frame_pixel_count_);
Henrik Boströmce0ea492020-01-13 10:27:18521 if (source_restrictor_->IncreaseFramerate(fps)) {
Henrik Boströmb08882b2020-01-07 09:11:17522 GetAdaptCounter().DecrementFramerate(reason, fps);
523 // Reset framerate in case of fewer fps steps down than up.
524 if (adapt_counter.FramerateCount() == 0 &&
525 fps != std::numeric_limits<int>::max()) {
526 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
Henrik Boströmce0ea492020-01-13 10:27:18527 source_restrictor_->IncreaseFramerate(
528 std::numeric_limits<int>::max());
Henrik Boströmb08882b2020-01-07 09:11:17529 }
530 break;
531 }
532 // Check if resolution should be increased based on bitrate.
533 if (reason == kQuality &&
534 !balanced_settings_.CanAdaptUpResolution(
Åsa Perssonf5e71e42020-01-09 09:04:53535 encoder_config_.codec_type, *last_frame_pixel_count_,
536 encoder_start_bitrate_bps_)) {
Henrik Boströmb08882b2020-01-07 09:11:17537 return;
538 }
539 // Scale up resolution.
Danil Chapovalov64f1f3f2020-01-16 13:41:10540 ABSL_FALLTHROUGH_INTENDED;
Henrik Boströmb08882b2020-01-07 09:11:17541 }
542 case DegradationPreference::MAINTAIN_FRAMERATE: {
543 // Check if resolution should be increased based on bitrate and
544 // limits specified by encoder capabilities.
545 if (reason == kQuality &&
546 !CanAdaptUpResolution(*last_frame_pixel_count_,
547 encoder_start_bitrate_bps_)) {
548 return;
549 }
550
551 // Scale up resolution.
552 int pixel_count = adaptation_request.input_pixel_count_;
553 if (adapt_counter.ResolutionCount() == 1) {
554 RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
555 pixel_count = std::numeric_limits<int>::max();
556 }
Henrik Boströmce0ea492020-01-13 10:27:18557 if (!source_restrictor_->RequestHigherResolutionThan(pixel_count))
Henrik Boströmb08882b2020-01-07 09:11:17558 return;
559 GetAdaptCounter().DecrementResolution(reason);
560 break;
561 }
562 case DegradationPreference::MAINTAIN_RESOLUTION: {
563 // Scale up framerate.
564 int fps = adaptation_request.framerate_fps_;
565 if (adapt_counter.FramerateCount() == 1) {
566 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
567 fps = std::numeric_limits<int>::max();
568 }
569
570 const int requested_framerate =
Henrik Boströmce0ea492020-01-13 10:27:18571 source_restrictor_->RequestHigherFramerateThan(fps);
Henrik Boströmb08882b2020-01-07 09:11:17572 if (requested_framerate == -1) {
Henrik Boströmb08882b2020-01-07 09:11:17573 return;
574 }
Henrik Boströmb08882b2020-01-07 09:11:17575 GetAdaptCounter().DecrementFramerate(reason);
576 break;
577 }
578 case DegradationPreference::DISABLED:
579 return;
580 }
581
Henrik Boströmd2382002020-01-10 14:44:01582 // Tell the adaptation listener to reconfigure the source for us according to
583 // the latest adaptation.
Henrik Boström07b17df2020-01-15 10:42:12584 MaybeUpdateVideoSourceRestrictions();
Henrik Boströmd2382002020-01-10 14:44:01585
Henrik Boströmb08882b2020-01-07 09:11:17586 last_adaptation_request_.emplace(adaptation_request);
587
588 UpdateAdaptationStats(reason);
589
590 RTC_LOG(LS_INFO) << adapt_counter.ToString();
591}
592
593bool OveruseFrameDetectorResourceAdaptationModule::AdaptDown(
594 AdaptReason reason) {
Henrik Boströma3d42522020-01-16 12:55:29595 if (!has_input_video_)
596 return false;
Henrik Boströmb08882b2020-01-07 09:11:17597 AdaptationRequest adaptation_request = {
598 *last_frame_pixel_count_, encoder_stats_observer_->GetInputFrameRate(),
599 AdaptationRequest::Mode::kAdaptDown};
600
601 bool downgrade_requested =
602 last_adaptation_request_ &&
603 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
604
605 bool did_adapt = true;
606
607 switch (EffectiveDegradataionPreference()) {
608 case DegradationPreference::BALANCED:
609 break;
610 case DegradationPreference::MAINTAIN_FRAMERATE:
611 if (downgrade_requested &&
612 adaptation_request.input_pixel_count_ >=
613 last_adaptation_request_->input_pixel_count_) {
614 // Don't request lower resolution if the current resolution is not
615 // lower than the last time we asked for the resolution to be lowered.
616 return true;
617 }
618 break;
619 case DegradationPreference::MAINTAIN_RESOLUTION:
620 if (adaptation_request.framerate_fps_ <= 0 ||
621 (downgrade_requested &&
622 adaptation_request.framerate_fps_ < kMinFramerateFps)) {
623 // If no input fps estimate available, can't determine how to scale down
624 // framerate. Otherwise, don't request lower framerate if we don't have
625 // a valid frame rate. Since framerate, unlike resolution, is a measure
626 // we have to estimate, and can fluctuate naturally over time, don't
627 // make the same kind of limitations as for resolution, but trust the
628 // overuse detector to not trigger too often.
629 return true;
630 }
631 break;
632 case DegradationPreference::DISABLED:
633 return true;
634 }
635
636 switch (EffectiveDegradataionPreference()) {
637 case DegradationPreference::BALANCED: {
638 // Try scale down framerate, if lower.
639 int fps = balanced_settings_.MinFps(encoder_config_.codec_type,
640 *last_frame_pixel_count_);
Henrik Boströmce0ea492020-01-13 10:27:18641 if (source_restrictor_->RestrictFramerate(fps)) {
Henrik Boströmb08882b2020-01-07 09:11:17642 GetAdaptCounter().IncrementFramerate(reason);
643 // Check if requested fps is higher (or close to) input fps.
644 absl::optional<int> min_diff =
645 balanced_settings_.MinFpsDiff(*last_frame_pixel_count_);
646 if (min_diff && adaptation_request.framerate_fps_ > 0) {
647 int fps_diff = adaptation_request.framerate_fps_ - fps;
648 if (fps_diff < min_diff.value()) {
649 did_adapt = false;
650 }
651 }
652 break;
653 }
654 // Scale down resolution.
Danil Chapovalov64f1f3f2020-01-16 13:41:10655 ABSL_FALLTHROUGH_INTENDED;
Henrik Boströmb08882b2020-01-07 09:11:17656 }
657 case DegradationPreference::MAINTAIN_FRAMERATE: {
658 // Scale down resolution.
659 bool min_pixels_reached = false;
Henrik Boströmce0ea492020-01-13 10:27:18660 if (!source_restrictor_->RequestResolutionLowerThan(
Henrik Boströmb08882b2020-01-07 09:11:17661 adaptation_request.input_pixel_count_,
662 encoder_->GetEncoderInfo().scaling_settings.min_pixels_per_frame,
663 &min_pixels_reached)) {
664 if (min_pixels_reached)
665 encoder_stats_observer_->OnMinPixelLimitReached();
666 return true;
667 }
668 GetAdaptCounter().IncrementResolution(reason);
669 break;
670 }
671 case DegradationPreference::MAINTAIN_RESOLUTION: {
672 // Scale down framerate.
Henrik Boströmce0ea492020-01-13 10:27:18673 const int requested_framerate =
674 source_restrictor_->RequestFramerateLowerThan(
675 adaptation_request.framerate_fps_);
Henrik Boströmb08882b2020-01-07 09:11:17676 if (requested_framerate == -1)
677 return true;
Henrik Boströmb08882b2020-01-07 09:11:17678 GetAdaptCounter().IncrementFramerate(reason);
679 break;
680 }
681 case DegradationPreference::DISABLED:
682 RTC_NOTREACHED();
683 }
684
Henrik Boströmd2382002020-01-10 14:44:01685 // Tell the adaptation listener to reconfigure the source for us according to
686 // the latest adaptation.
Henrik Boström07b17df2020-01-15 10:42:12687 MaybeUpdateVideoSourceRestrictions();
Henrik Boströmd2382002020-01-10 14:44:01688
Henrik Boströmb08882b2020-01-07 09:11:17689 last_adaptation_request_.emplace(adaptation_request);
690
691 UpdateAdaptationStats(reason);
692
693 RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
694 return did_adapt;
695}
696
Henrik Boström8234b922020-01-13 16:26:50697void OveruseFrameDetectorResourceAdaptationModule::
Henrik Boström07b17df2020-01-15 10:42:12698 MaybeUpdateVideoSourceRestrictions() {
699 VideoSourceRestrictions new_restrictions = ApplyDegradationPreference(
700 source_restrictor_->source_restrictions(), degradation_preference_);
701 if (video_source_restrictions_ != new_restrictions) {
702 video_source_restrictions_ = std::move(new_restrictions);
Henrik Boström8234b922020-01-13 16:26:50703 adaptation_listener_->OnVideoSourceRestrictionsUpdated(
Henrik Boström07b17df2020-01-15 10:42:12704 video_source_restrictions_);
Henrik Boströmfae6f0e2020-01-20 10:16:50705 MaybeUpdateTargetFrameRate();
706 }
707}
708
709void OveruseFrameDetectorResourceAdaptationModule::
710 MaybeUpdateTargetFrameRate() {
711 // The current target framerate is the maximum frame rate as specified by
712 // the current codec configuration or any limit imposed by the adaptation
713 // module. This is used to make sure overuse detection doesn't needlessly
714 // trigger in low and/or variable framerate scenarios.
715 absl::optional<double> target_frame_rate =
716 ApplyDegradationPreference(source_restrictor_->source_restrictions(),
717 degradation_preference_)
718 .max_frame_rate();
719 if (!target_frame_rate.has_value() ||
720 (codec_max_frame_rate_.has_value() &&
721 codec_max_frame_rate_.value() < target_frame_rate.value())) {
722 target_frame_rate = codec_max_frame_rate_;
723 }
724 if (target_frame_rate != target_frame_rate_) {
725 target_frame_rate_ = target_frame_rate;
726 if (overuse_detector_is_started_) {
727 overuse_detector_->OnTargetFramerateUpdated(
728 target_frame_rate_.has_value()
729 ? static_cast<int>(target_frame_rate_.value())
730 : std::numeric_limits<int>::max());
731 }
Henrik Boström8234b922020-01-13 16:26:50732 }
733}
734
Henrik Boströmb08882b2020-01-07 09:11:17735// TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged.
736void OveruseFrameDetectorResourceAdaptationModule::UpdateAdaptationStats(
737 AdaptReason reason) {
738 switch (reason) {
739 case kCpu:
740 encoder_stats_observer_->OnAdaptationChanged(
741 VideoStreamEncoderObserver::AdaptationReason::kCpu,
742 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
743 break;
744 case kQuality:
745 encoder_stats_observer_->OnAdaptationChanged(
746 VideoStreamEncoderObserver::AdaptationReason::kQuality,
747 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
748 break;
749 }
750}
751
752VideoStreamEncoderObserver::AdaptationSteps
753OveruseFrameDetectorResourceAdaptationModule::GetActiveCounts(
754 AdaptReason reason) {
Henrik Boströmb08882b2020-01-07 09:11:17755 VideoStreamEncoderObserver::AdaptationSteps counts =
756 GetConstAdaptCounter().Counts(reason);
757 switch (reason) {
758 case kCpu:
759 if (!IsFramerateScalingEnabled(degradation_preference_))
760 counts.num_framerate_reductions = absl::nullopt;
761 if (!IsResolutionScalingEnabled(degradation_preference_))
762 counts.num_resolution_reductions = absl::nullopt;
763 break;
764 case kQuality:
765 if (!IsFramerateScalingEnabled(degradation_preference_) ||
766 !is_quality_scaler_enabled_) {
767 counts.num_framerate_reductions = absl::nullopt;
768 }
769 if (!IsResolutionScalingEnabled(degradation_preference_) ||
770 !is_quality_scaler_enabled_) {
771 counts.num_resolution_reductions = absl::nullopt;
772 }
773 break;
774 }
775 return counts;
776}
777
778DegradationPreference OveruseFrameDetectorResourceAdaptationModule::
779 EffectiveDegradataionPreference() {
780 // Balanced mode for screenshare works via automatic animation detection:
781 // Resolution is capped for fullscreen animated content.
782 // Adapatation is done only via framerate downgrade.
783 // Thus effective degradation preference is MAINTAIN_RESOLUTION.
784 return (encoder_config_.content_type ==
785 VideoEncoderConfig::ContentType::kScreen &&
786 degradation_preference_ == DegradationPreference::BALANCED)
787 ? DegradationPreference::MAINTAIN_RESOLUTION
788 : degradation_preference_;
789}
790
791OveruseFrameDetectorResourceAdaptationModule::AdaptCounter&
792OveruseFrameDetectorResourceAdaptationModule::GetAdaptCounter() {
793 return adapt_counters_[degradation_preference_];
794}
795
796const OveruseFrameDetectorResourceAdaptationModule::AdaptCounter&
797OveruseFrameDetectorResourceAdaptationModule::GetConstAdaptCounter() {
Henrik Boströmb08882b2020-01-07 09:11:17798 return adapt_counters_[degradation_preference_];
799}
800
801absl::optional<VideoEncoder::QpThresholds>
802OveruseFrameDetectorResourceAdaptationModule::GetQpThresholds() const {
Henrik Boströmb08882b2020-01-07 09:11:17803 RTC_DCHECK(last_frame_pixel_count_.has_value());
804 return balanced_settings_.GetQpThresholds(encoder_config_.codec_type,
805 last_frame_pixel_count_.value());
806}
807
808bool OveruseFrameDetectorResourceAdaptationModule::CanAdaptUpResolution(
809 int pixels,
810 uint32_t bitrate_bps) const {
811 absl::optional<VideoEncoder::ResolutionBitrateLimits> bitrate_limits =
Henrik Boströmce0ea492020-01-13 10:27:18812 GetEncoderBitrateLimits(
813 encoder_->GetEncoderInfo(),
814 source_restrictor_->GetHigherResolutionThan(pixels));
Henrik Boströmb08882b2020-01-07 09:11:17815 if (!bitrate_limits.has_value() || bitrate_bps == 0) {
816 return true; // No limit configured or bitrate provided.
817 }
818 RTC_DCHECK_GE(bitrate_limits->frame_size_pixels, pixels);
819 return bitrate_bps >=
820 static_cast<uint32_t>(bitrate_limits->min_start_bitrate_bps);
821}
822
823} // namespace webrtc