blob: 673eebd09e995d1a117e76b4f4d2e6de709b6e02 [file] [log] [blame]
Henrik Boströmb08882b2020-01-07 10:11:17 +01001/*
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"
20#include "api/video/video_source_interface.h"
Henrik Boströmce0ea492020-01-13 11:27:18 +010021#include "call/adaptation/video_source_restrictions.h"
Henrik Boströmb08882b2020-01-07 10:11:17 +010022#include "rtc_base/logging.h"
Henrik Boströmd2382002020-01-10 15:44:01 +010023#include "rtc_base/numerics/safe_conversions.h"
Henrik Boströmb08882b2020-01-07 10:11:17 +010024#include "rtc_base/strings/string_builder.h"
25#include "rtc_base/system/fallthrough.h"
26#include "video/video_stream_encoder.h"
27
28namespace webrtc {
29
30namespace {
31
32const int kMinFramerateFps = 2;
33
34bool IsResolutionScalingEnabled(DegradationPreference degradation_preference) {
35 return degradation_preference == DegradationPreference::MAINTAIN_FRAMERATE ||
36 degradation_preference == DegradationPreference::BALANCED;
37}
38
39bool IsFramerateScalingEnabled(DegradationPreference degradation_preference) {
40 return degradation_preference == DegradationPreference::MAINTAIN_RESOLUTION ||
41 degradation_preference == DegradationPreference::BALANCED;
42}
43
44} // namespace
45
Henrik Boströmce0ea492020-01-13 11:27:18 +010046// VideoSourceRestrictor is responsible for keeping track of current
47// VideoSourceRestrictions and how to modify them in response to adapting up or
48// down. It is not reponsible for determining when we should adapt up or down -
49// for that, see OveruseFrameDetectorResourceAdaptationModule::AdaptUp() and
50// AdaptDown() - only how to modify the source/sink restrictions when this
51// happens. Note that it is also not responsible for reconfigruring the
52// source/sink, it is only a keeper of desired restrictions.
53//
54// Thread safety is ensured between SetHasInputVideoAndDegradationPreference()
55// calls on the worker thread and adaptation logic on the encoder task queue
56// using a lock.
57class OveruseFrameDetectorResourceAdaptationModule::VideoSourceRestrictor {
Henrik Boströmb08882b2020-01-07 10:11:17 +010058 public:
Henrik Boströmce0ea492020-01-13 11:27:18 +010059 explicit VideoSourceRestrictor(
60 VideoSourceSinkController* video_source_sink_controller)
61 : video_source_sink_controller_(video_source_sink_controller),
62 has_input_video_(false),
63 degradation_preference_(DegradationPreference::DISABLED) {}
Henrik Boströmb08882b2020-01-07 10:11:17 +010064
Henrik Boströmce0ea492020-01-13 11:27:18 +010065 VideoSourceRestrictions source_restrictions() {
Henrik Boströmd2382002020-01-10 15:44:01 +010066 rtc::CritScope lock(&crit_);
Henrik Boströmce0ea492020-01-13 11:27:18 +010067 return source_restrictions_;
Henrik Boströmd2382002020-01-10 15:44:01 +010068 }
69
Henrik Boströmce0ea492020-01-13 11:27:18 +010070 // Inform the restrictor of new source status and degradation preference.
71 // TODO(hbos): Can this be moved to the encoder queue? If so, the |crit_| lock
72 // can be removed and we only need a sequence checker.
73 void SetHasInputVideoAndDegradationPreference(
74 bool has_input_video,
75 DegradationPreference degradation_preference) {
Henrik Boströmb08882b2020-01-07 10:11:17 +010076 // Called on libjingle's worker thread.
77 RTC_DCHECK_RUN_ON(&main_checker_);
Henrik Boströmce0ea492020-01-13 11:27:18 +010078 rtc::CritScope lock(&crit_);
79 has_input_video_ = has_input_video;
80 degradation_preference_ = degradation_preference;
Henrik Boströmb08882b2020-01-07 10:11:17 +010081 }
82
Henrik Boströmd2382002020-01-10 15:44:01 +010083 // Informs the sink of the new source settings.
Henrik Boströmce0ea492020-01-13 11:27:18 +010084 // TODO(https://crbug.com/webrtc/11222): Handle all sink updates in
85 // video_stream_encoder.cc. This method is only used when setting the
86 // degradation preference such that it moves in or out of the "balanced"
87 // state, or when clearing all counters. When moving the remaining degradation
88 // preference logic inside the VideoSourceSinkController to here, stop
89 // explicitly setting the controller's restrictions and instead inform the
90 // VideoStreamEncoder of updated restrictions using
91 // OnVideoSourceRestrictionsUpdated().
Henrik Boströmb08882b2020-01-07 10:11:17 +010092 void ResetPixelFpsCount() {
93 rtc::CritScope lock(&crit_);
Henrik Boströmce0ea492020-01-13 11:27:18 +010094 // Clear all restrictions.
95 source_restrictions_ = VideoSourceRestrictions();
96 video_source_sink_controller_->SetRestrictions(source_restrictions_);
97 video_source_sink_controller_->PushSourceSinkSettings();
Henrik Boströmb08882b2020-01-07 10:11:17 +010098 }
99
Henrik Boströmce0ea492020-01-13 11:27:18 +0100100 // Updates the source_restrictions(). The source/sink has to be informed of
101 // this separately.
Henrik Boströmb08882b2020-01-07 10:11:17 +0100102 bool RequestResolutionLowerThan(int pixel_count,
103 int min_pixels_per_frame,
104 bool* min_pixels_reached) {
105 // Called on the encoder task queue.
106 rtc::CritScope lock(&crit_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100107 if (!has_input_video_ ||
108 !IsResolutionScalingEnabled(degradation_preference_)) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100109 // This can happen since |degradation_preference_| is set on libjingle's
110 // worker thread but the adaptation is done on the encoder task queue.
111 return false;
112 }
113 // The input video frame size will have a resolution less than or equal to
114 // |max_pixel_count| depending on how the source can scale the frame size.
115 const int pixels_wanted = (pixel_count * 3) / 5;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100116 if (pixels_wanted >=
117 rtc::dchecked_cast<int>(
118 source_restrictions_.max_pixels_per_frame().value_or(
119 std::numeric_limits<int>::max()))) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100120 return false;
121 }
122 if (pixels_wanted < min_pixels_per_frame) {
123 *min_pixels_reached = true;
124 return false;
125 }
126 RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: "
127 << pixels_wanted;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100128 source_restrictions_.set_max_pixels_per_frame(
129 pixels_wanted != std::numeric_limits<int>::max()
130 ? absl::optional<size_t>(pixels_wanted)
131 : absl::nullopt);
132 source_restrictions_.set_target_pixels_per_frame(absl::nullopt);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100133 return true;
134 }
135
Henrik Boströmce0ea492020-01-13 11:27:18 +0100136 // Updates the source_restrictions(). The source/sink has to be informed of
137 // this separately.
Henrik Boströmb08882b2020-01-07 10:11:17 +0100138 int RequestFramerateLowerThan(int fps) {
139 // Called on the encoder task queue.
140 // The input video frame rate will be scaled down to 2/3, rounding down.
141 int framerate_wanted = (fps * 2) / 3;
142 return RestrictFramerate(framerate_wanted) ? framerate_wanted : -1;
143 }
144
145 int GetHigherResolutionThan(int pixel_count) const {
146 // On step down we request at most 3/5 the pixel count of the previous
147 // resolution, so in order to take "one step up" we request a resolution
148 // as close as possible to 5/3 of the current resolution. The actual pixel
149 // count selected depends on the capabilities of the source. In order to
150 // not take a too large step up, we cap the requested pixel count to be at
151 // most four time the current number of pixels.
152 return (pixel_count * 5) / 3;
153 }
154
Henrik Boströmce0ea492020-01-13 11:27:18 +0100155 // Updates the source_restrictions(). The source/sink has to be informed of
156 // this separately.
Henrik Boströmb08882b2020-01-07 10:11:17 +0100157 bool RequestHigherResolutionThan(int pixel_count) {
158 // Called on the encoder task queue.
159 rtc::CritScope lock(&crit_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100160 if (!has_input_video_ ||
161 !IsResolutionScalingEnabled(degradation_preference_)) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100162 // This can happen since |degradation_preference_| is set on libjingle's
163 // worker thread but the adaptation is done on the encoder task queue.
164 return false;
165 }
166 int max_pixels_wanted = pixel_count;
167 if (max_pixels_wanted != std::numeric_limits<int>::max())
168 max_pixels_wanted = pixel_count * 4;
169
Henrik Boströmce0ea492020-01-13 11:27:18 +0100170 if (max_pixels_wanted <=
171 rtc::dchecked_cast<int>(
172 source_restrictions_.max_pixels_per_frame().value_or(
173 std::numeric_limits<int>::max()))) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100174 return false;
Henrik Boströmb08882b2020-01-07 10:11:17 +0100175 }
Henrik Boströmce0ea492020-01-13 11:27:18 +0100176
Henrik Boströmb08882b2020-01-07 10:11:17 +0100177 RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
178 << max_pixels_wanted;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100179 source_restrictions_.set_max_pixels_per_frame(
180 max_pixels_wanted != std::numeric_limits<int>::max()
181 ? absl::optional<size_t>(max_pixels_wanted)
182 : absl::nullopt);
183 source_restrictions_.set_target_pixels_per_frame(
184 max_pixels_wanted != std::numeric_limits<int>::max()
185 ? absl::optional<size_t>(GetHigherResolutionThan(pixel_count))
186 : absl::nullopt);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100187 return true;
188 }
189
Henrik Boströmce0ea492020-01-13 11:27:18 +0100190 // Updates the source_restrictions(). The source/sink has to be informed of
191 // this separately.
Henrik Boströmb08882b2020-01-07 10:11:17 +0100192 // Request upgrade in framerate. Returns the new requested frame, or -1 if
193 // no change requested. Note that maxint may be returned if limits due to
194 // adaptation requests are removed completely. In that case, consider
195 // |max_framerate_| to be the current limit (assuming the capturer complies).
196 int RequestHigherFramerateThan(int fps) {
197 // Called on the encoder task queue.
198 // The input frame rate will be scaled up to the last step, with rounding.
199 int framerate_wanted = fps;
200 if (fps != std::numeric_limits<int>::max())
201 framerate_wanted = (fps * 3) / 2;
202
203 return IncreaseFramerate(framerate_wanted) ? framerate_wanted : -1;
204 }
205
Henrik Boströmce0ea492020-01-13 11:27:18 +0100206 // Updates the source_restrictions(). The source/sink has to be informed of
207 // this separately.
Henrik Boströmb08882b2020-01-07 10:11:17 +0100208 bool RestrictFramerate(int fps) {
209 // Called on the encoder task queue.
210 rtc::CritScope lock(&crit_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100211 if (!has_input_video_ ||
212 !IsFramerateScalingEnabled(degradation_preference_))
Henrik Boströmb08882b2020-01-07 10:11:17 +0100213 return false;
214
215 const int fps_wanted = std::max(kMinFramerateFps, fps);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100216 if (fps_wanted >=
217 rtc::dchecked_cast<int>(source_restrictions_.max_frame_rate().value_or(
218 std::numeric_limits<int>::max())))
Henrik Boströmb08882b2020-01-07 10:11:17 +0100219 return false;
220
221 RTC_LOG(LS_INFO) << "Scaling down framerate: " << fps_wanted;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100222 source_restrictions_.set_max_frame_rate(
223 fps_wanted != std::numeric_limits<int>::max()
224 ? absl::optional<double>(fps_wanted)
225 : absl::nullopt);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100226 return true;
227 }
228
Henrik Boströmce0ea492020-01-13 11:27:18 +0100229 // Updates the source_restrictions(). The source/sink has to be informed of
230 // this separately.
Henrik Boströmb08882b2020-01-07 10:11:17 +0100231 bool IncreaseFramerate(int fps) {
232 // Called on the encoder task queue.
233 rtc::CritScope lock(&crit_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100234 if (!has_input_video_ ||
235 !IsFramerateScalingEnabled(degradation_preference_))
Henrik Boströmb08882b2020-01-07 10:11:17 +0100236 return false;
237
238 const int fps_wanted = std::max(kMinFramerateFps, fps);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100239 if (fps_wanted <=
240 rtc::dchecked_cast<int>(source_restrictions_.max_frame_rate().value_or(
241 std::numeric_limits<int>::max())))
Henrik Boströmb08882b2020-01-07 10:11:17 +0100242 return false;
243
244 RTC_LOG(LS_INFO) << "Scaling up framerate: " << fps_wanted;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100245 source_restrictions_.set_max_frame_rate(
246 fps_wanted != std::numeric_limits<int>::max()
247 ? absl::optional<double>(fps_wanted)
248 : absl::nullopt);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100249 return true;
250 }
251
252 private:
Henrik Boströmb08882b2020-01-07 10:11:17 +0100253 rtc::CriticalSection crit_;
254 SequenceChecker main_checker_;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100255 VideoSourceSinkController* const video_source_sink_controller_;
256 VideoSourceRestrictions source_restrictions_ RTC_GUARDED_BY(&crit_);
257 bool has_input_video_ RTC_GUARDED_BY(&crit_);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100258 DegradationPreference degradation_preference_ RTC_GUARDED_BY(&crit_);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100259
Henrik Boströmce0ea492020-01-13 11:27:18 +0100260 RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceRestrictor);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100261};
262
263// Class holding adaptation information.
264OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::AdaptCounter() {
265 fps_counters_.resize(kScaleReasonSize);
266 resolution_counters_.resize(kScaleReasonSize);
267 static_assert(kScaleReasonSize == 2, "Update MoveCount.");
268}
269
270OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::~AdaptCounter() {}
271
272std::string
273OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString() const {
274 rtc::StringBuilder ss;
275 ss << "Downgrade counts: fps: {" << ToString(fps_counters_);
276 ss << "}, resolution: {" << ToString(resolution_counters_) << "}";
277 return ss.Release();
278}
279
280VideoStreamEncoderObserver::AdaptationSteps
281OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Counts(
282 int reason) const {
283 VideoStreamEncoderObserver::AdaptationSteps counts;
284 counts.num_framerate_reductions = fps_counters_[reason];
285 counts.num_resolution_reductions = resolution_counters_[reason];
286 return counts;
287}
288
289void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
290 IncrementFramerate(int reason) {
291 ++(fps_counters_[reason]);
292}
293
294void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
295 IncrementResolution(int reason) {
296 ++(resolution_counters_[reason]);
297}
298
299void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
300 DecrementFramerate(int reason) {
301 if (fps_counters_[reason] == 0) {
302 // Balanced mode: Adapt up is in a different order, switch reason.
303 // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3).
304 // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0}
305 // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0}
306 // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0}
307 // 4. Up resolution (quality): res={quality:0,cpu:0}, fps={quality:0,cpu:0}
308 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
309 RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded.";
310 MoveCount(&resolution_counters_, reason);
311 MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize);
312 }
313 --(fps_counters_[reason]);
314 RTC_DCHECK_GE(fps_counters_[reason], 0);
315}
316
317void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
318 DecrementResolution(int reason) {
319 if (resolution_counters_[reason] == 0) {
320 // Balanced mode: Adapt up is in a different order, switch reason.
321 RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason.";
322 RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded.";
323 MoveCount(&fps_counters_, reason);
324 MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize);
325 }
326 --(resolution_counters_[reason]);
327 RTC_DCHECK_GE(resolution_counters_[reason], 0);
328}
329
330void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
331 DecrementFramerate(int reason, int cur_fps) {
332 DecrementFramerate(reason);
333 // Reset if at max fps (i.e. in case of fewer steps up than down).
334 if (cur_fps == std::numeric_limits<int>::max())
335 absl::c_fill(fps_counters_, 0);
336}
337
338int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount()
339 const {
340 return Count(fps_counters_);
341}
342
343int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::
344 ResolutionCount() const {
345 return Count(resolution_counters_);
346}
347
348int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount(
349 int reason) const {
350 return fps_counters_[reason];
351}
352
353int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ResolutionCount(
354 int reason) const {
355 return resolution_counters_[reason];
356}
357
358int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::TotalCount(
359 int reason) const {
360 return FramerateCount(reason) + ResolutionCount(reason);
361}
362
363int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Count(
364 const std::vector<int>& counters) const {
365 return absl::c_accumulate(counters, 0);
366}
367
368void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::MoveCount(
369 std::vector<int>* counters,
370 int from_reason) {
371 int to_reason = (from_reason + 1) % kScaleReasonSize;
372 ++((*counters)[to_reason]);
373 --((*counters)[from_reason]);
374}
375
376std::string
377OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString(
378 const std::vector<int>& counters) const {
379 rtc::StringBuilder ss;
380 for (size_t reason = 0; reason < kScaleReasonSize; ++reason) {
381 ss << (reason ? " cpu" : "quality") << ":" << counters[reason];
382 }
383 return ss.Release();
384}
385
386OveruseFrameDetectorResourceAdaptationModule::
387 OveruseFrameDetectorResourceAdaptationModule(
Henrik Boström382cc6d2020-01-07 10:15:04 +0100388 VideoStreamEncoder* video_stream_encoder,
Henrik Boströmce0ea492020-01-13 11:27:18 +0100389 VideoSourceSinkController* video_source_sink_controller,
Henrik Boströmb08882b2020-01-07 10:11:17 +0100390 std::unique_ptr<OveruseFrameDetector> overuse_detector,
Henrik Boströmd2382002020-01-10 15:44:01 +0100391 VideoStreamEncoderObserver* encoder_stats_observer,
392 ResourceAdaptationModuleListener* adaptation_listener)
Henrik Boströmb08882b2020-01-07 10:11:17 +0100393 : encoder_queue_(nullptr),
Henrik Boströmd2382002020-01-10 15:44:01 +0100394 adaptation_listener_(adaptation_listener),
Henrik Boström382cc6d2020-01-07 10:15:04 +0100395 video_stream_encoder_(video_stream_encoder),
Henrik Boströmce0ea492020-01-13 11:27:18 +0100396 video_source_sink_controller_(video_source_sink_controller),
Henrik Boströmb08882b2020-01-07 10:11:17 +0100397 degradation_preference_(DegradationPreference::DISABLED),
398 adapt_counters_(),
399 balanced_settings_(),
400 last_adaptation_request_(absl::nullopt),
401 last_frame_pixel_count_(absl::nullopt),
Henrik Boströmce0ea492020-01-13 11:27:18 +0100402 source_restrictor_(std::make_unique<VideoSourceRestrictor>(
403 video_source_sink_controller)),
Henrik Boströmb08882b2020-01-07 10:11:17 +0100404 overuse_detector_(std::move(overuse_detector)),
405 codec_max_framerate_(-1),
406 encoder_start_bitrate_bps_(0),
407 is_quality_scaler_enabled_(false),
408 encoder_config_(),
409 encoder_(nullptr),
410 encoder_stats_observer_(encoder_stats_observer) {
Henrik Boströmd2382002020-01-10 15:44:01 +0100411 RTC_DCHECK(adaptation_listener_);
Henrik Boström382cc6d2020-01-07 10:15:04 +0100412 RTC_DCHECK(video_stream_encoder_);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100413 RTC_DCHECK(overuse_detector_);
414 RTC_DCHECK(encoder_stats_observer_);
415}
416
417OveruseFrameDetectorResourceAdaptationModule::
418 ~OveruseFrameDetectorResourceAdaptationModule() {}
419
420void OveruseFrameDetectorResourceAdaptationModule::Initialize(
421 rtc::TaskQueue* encoder_queue) {
422 RTC_DCHECK(!encoder_queue_);
423 encoder_queue_ = encoder_queue;
424 RTC_DCHECK(encoder_queue_);
425}
426
427void OveruseFrameDetectorResourceAdaptationModule::SetEncoder(
428 VideoEncoder* encoder) {
429 RTC_DCHECK(encoder_queue_);
430 RTC_DCHECK_RUN_ON(encoder_queue_);
431 encoder_ = encoder;
432}
433
Henrik Boströmd2382002020-01-10 15:44:01 +0100434void OveruseFrameDetectorResourceAdaptationModule::StartCheckForOveruse(
435 ResourceAdaptationModuleListener* adaptation_listener) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100436 RTC_DCHECK(encoder_queue_);
437 RTC_DCHECK_RUN_ON(encoder_queue_);
438 RTC_DCHECK(encoder_);
Henrik Boströmd2382002020-01-10 15:44:01 +0100439 // TODO(hbos): When AdaptUp() and AdaptDown() are no longer invoked outside
440 // the interval between StartCheckForOveruse() and StopCheckForOveruse(),
441 // support configuring which |adaptation_listener_| to use on the fly. It is
442 // currently hardcoded for the entire lifetime of the module in order to
443 // support adaptation caused by VideoStreamEncoder or QualityScaler invoking
444 // AdaptUp() and AdaptDown() even when the OveruseDetector is inactive.
445 RTC_DCHECK_EQ(adaptation_listener, adaptation_listener_);
Henrik Boström382cc6d2020-01-07 10:15:04 +0100446 overuse_detector_->StartCheckForOveruse(
447 encoder_queue_, video_stream_encoder_->GetCpuOveruseOptions(), this);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100448}
449
450void OveruseFrameDetectorResourceAdaptationModule::StopCheckForOveruse() {
451 RTC_DCHECK(encoder_queue_);
452 RTC_DCHECK_RUN_ON(encoder_queue_);
453 overuse_detector_->StopCheckForOveruse();
454}
455
456void OveruseFrameDetectorResourceAdaptationModule::FrameCaptured(
457 const VideoFrame& frame,
458 int64_t time_when_first_seen_us) {
459 RTC_DCHECK(encoder_queue_);
460 RTC_DCHECK_RUN_ON(encoder_queue_);
461 overuse_detector_->FrameCaptured(frame, time_when_first_seen_us);
462}
463
464void OveruseFrameDetectorResourceAdaptationModule::FrameSent(
465 uint32_t timestamp,
466 int64_t time_sent_in_us,
467 int64_t capture_time_us,
468 absl::optional<int> encode_duration_us) {
469 RTC_DCHECK(encoder_queue_);
470 RTC_DCHECK_RUN_ON(encoder_queue_);
471 overuse_detector_->FrameSent(timestamp, time_sent_in_us, capture_time_us,
472 encode_duration_us);
473}
474
475void OveruseFrameDetectorResourceAdaptationModule::SetLastFramePixelCount(
476 absl::optional<int> last_frame_pixel_count) {
477 RTC_DCHECK(encoder_queue_);
478 RTC_DCHECK_RUN_ON(encoder_queue_);
479 last_frame_pixel_count_ = last_frame_pixel_count;
480}
481
482void OveruseFrameDetectorResourceAdaptationModule::SetEncoderConfig(
483 VideoEncoderConfig encoder_config) {
484 RTC_DCHECK(encoder_queue_);
485 RTC_DCHECK_RUN_ON(encoder_queue_);
486 encoder_config_ = std::move(encoder_config);
487}
488
489void OveruseFrameDetectorResourceAdaptationModule::SetCodecMaxFramerate(
490 int codec_max_framerate) {
491 RTC_DCHECK(encoder_queue_);
492 RTC_DCHECK_RUN_ON(encoder_queue_);
493 codec_max_framerate_ = codec_max_framerate;
494}
495
496void OveruseFrameDetectorResourceAdaptationModule::SetEncoderStartBitrateBps(
497 uint32_t encoder_start_bitrate_bps) {
498 RTC_DCHECK(encoder_queue_);
499 RTC_DCHECK_RUN_ON(encoder_queue_);
500 encoder_start_bitrate_bps_ = encoder_start_bitrate_bps;
501}
502
503void OveruseFrameDetectorResourceAdaptationModule::SetIsQualityScalerEnabled(
504 bool is_quality_scaler_enabled) {
505 RTC_DCHECK(encoder_queue_);
506 RTC_DCHECK_RUN_ON(encoder_queue_);
507 is_quality_scaler_enabled_ = is_quality_scaler_enabled;
508}
509
Henrik Boströmce0ea492020-01-13 11:27:18 +0100510void OveruseFrameDetectorResourceAdaptationModule::
511 SetHasInputVideoAndDegradationPreference(
512 bool has_input_video,
513 DegradationPreference degradation_preference) {
514 source_restrictor_->SetHasInputVideoAndDegradationPreference(
515 has_input_video, degradation_preference);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100516 encoder_queue_->PostTask([this, degradation_preference] {
517 RTC_DCHECK_RUN_ON(encoder_queue_);
518 if (degradation_preference_ != degradation_preference) {
519 // Reset adaptation state, so that we're not tricked into thinking there's
520 // an already pending request of the same type.
521 last_adaptation_request_.reset();
522 if (degradation_preference == DegradationPreference::BALANCED ||
523 degradation_preference_ == DegradationPreference::BALANCED) {
524 // TODO(asapersson): Consider removing |adapt_counters_| map and use one
525 // AdaptCounter for all modes.
Henrik Boströmce0ea492020-01-13 11:27:18 +0100526 source_restrictor_->ResetPixelFpsCount();
Henrik Boströmb08882b2020-01-07 10:11:17 +0100527 adapt_counters_.clear();
528 }
529 }
530 degradation_preference_ = degradation_preference;
531 });
532}
533
Henrik Boströmb08882b2020-01-07 10:11:17 +0100534void OveruseFrameDetectorResourceAdaptationModule::RefreshTargetFramerate() {
535 RTC_DCHECK(encoder_queue_);
536 RTC_DCHECK_RUN_ON(encoder_queue_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100537 // We need the "sink wants" from the |video_source_sink_controller_| because
538 // the controller filters its current settings as "sink wants" differently
539 // depending degradation preferences.
540 // TODO(https://crbug.com/webrtc/11222): When degradation preference-related
541 // changes to settings are handled by this class instead, we can remove the
542 // dependency on the controller; the VideoSourceRestrictions outputted by this
543 // module will then be the "final" settings, including the max frame rate.
544 auto sink_wants = video_source_sink_controller_->CurrentSettingsToSinkWants();
Henrik Boströmb08882b2020-01-07 10:11:17 +0100545 // Get the current target framerate, ie the maximum framerate as specified by
546 // the current codec configuration, or any limit imposed by cpu adaption in
547 // maintain-resolution or balanced mode. This is used to make sure overuse
548 // detection doesn't needlessly trigger in low and/or variable framerate
549 // scenarios.
550 int target_framerate =
Henrik Boströmce0ea492020-01-13 11:27:18 +0100551 std::min(codec_max_framerate_, sink_wants.max_framerate_fps);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100552 overuse_detector_->OnTargetFramerateUpdated(target_framerate);
553}
554
555void OveruseFrameDetectorResourceAdaptationModule::ResetAdaptationCounters() {
556 RTC_DCHECK(encoder_queue_);
557 RTC_DCHECK_RUN_ON(encoder_queue_);
558 last_adaptation_request_.reset();
Henrik Boströmce0ea492020-01-13 11:27:18 +0100559 source_restrictor_->ResetPixelFpsCount();
Henrik Boströmb08882b2020-01-07 10:11:17 +0100560 adapt_counters_.clear();
561}
562
563void OveruseFrameDetectorResourceAdaptationModule::AdaptUp(AdaptReason reason) {
564 RTC_DCHECK(encoder_queue_);
565 RTC_DCHECK_RUN_ON(encoder_queue_);
566 const AdaptCounter& adapt_counter = GetConstAdaptCounter();
567 int num_downgrades = adapt_counter.TotalCount(reason);
568 if (num_downgrades == 0)
569 return;
570 RTC_DCHECK_GT(num_downgrades, 0);
571
572 AdaptationRequest adaptation_request = {
573 *last_frame_pixel_count_, encoder_stats_observer_->GetInputFrameRate(),
574 AdaptationRequest::Mode::kAdaptUp};
575
576 bool adapt_up_requested =
577 last_adaptation_request_ &&
578 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
579
580 if (EffectiveDegradataionPreference() ==
581 DegradationPreference::MAINTAIN_FRAMERATE) {
582 if (adapt_up_requested &&
583 adaptation_request.input_pixel_count_ <=
584 last_adaptation_request_->input_pixel_count_) {
585 // Don't request higher resolution if the current resolution is not
586 // higher than the last time we asked for the resolution to be higher.
587 return;
588 }
589 }
590
591 switch (EffectiveDegradataionPreference()) {
592 case DegradationPreference::BALANCED: {
593 // Check if quality should be increased based on bitrate.
594 if (reason == kQuality &&
Åsa Perssonf5e71e42020-01-09 10:04:53 +0100595 !balanced_settings_.CanAdaptUp(encoder_config_.codec_type,
596 *last_frame_pixel_count_,
Henrik Boströmb08882b2020-01-07 10:11:17 +0100597 encoder_start_bitrate_bps_)) {
598 return;
599 }
600 // Try scale up framerate, if higher.
601 int fps = balanced_settings_.MaxFps(encoder_config_.codec_type,
602 *last_frame_pixel_count_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100603 if (source_restrictor_->IncreaseFramerate(fps)) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100604 GetAdaptCounter().DecrementFramerate(reason, fps);
605 // Reset framerate in case of fewer fps steps down than up.
606 if (adapt_counter.FramerateCount() == 0 &&
607 fps != std::numeric_limits<int>::max()) {
608 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
Henrik Boströmce0ea492020-01-13 11:27:18 +0100609 source_restrictor_->IncreaseFramerate(
610 std::numeric_limits<int>::max());
Henrik Boströmb08882b2020-01-07 10:11:17 +0100611 }
612 break;
613 }
614 // Check if resolution should be increased based on bitrate.
615 if (reason == kQuality &&
616 !balanced_settings_.CanAdaptUpResolution(
Åsa Perssonf5e71e42020-01-09 10:04:53 +0100617 encoder_config_.codec_type, *last_frame_pixel_count_,
618 encoder_start_bitrate_bps_)) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100619 return;
620 }
621 // Scale up resolution.
622 RTC_FALLTHROUGH();
623 }
624 case DegradationPreference::MAINTAIN_FRAMERATE: {
625 // Check if resolution should be increased based on bitrate and
626 // limits specified by encoder capabilities.
627 if (reason == kQuality &&
628 !CanAdaptUpResolution(*last_frame_pixel_count_,
629 encoder_start_bitrate_bps_)) {
630 return;
631 }
632
633 // Scale up resolution.
634 int pixel_count = adaptation_request.input_pixel_count_;
635 if (adapt_counter.ResolutionCount() == 1) {
636 RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
637 pixel_count = std::numeric_limits<int>::max();
638 }
Henrik Boströmce0ea492020-01-13 11:27:18 +0100639 if (!source_restrictor_->RequestHigherResolutionThan(pixel_count))
Henrik Boströmb08882b2020-01-07 10:11:17 +0100640 return;
641 GetAdaptCounter().DecrementResolution(reason);
642 break;
643 }
644 case DegradationPreference::MAINTAIN_RESOLUTION: {
645 // Scale up framerate.
646 int fps = adaptation_request.framerate_fps_;
647 if (adapt_counter.FramerateCount() == 1) {
648 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
649 fps = std::numeric_limits<int>::max();
650 }
651
652 const int requested_framerate =
Henrik Boströmce0ea492020-01-13 11:27:18 +0100653 source_restrictor_->RequestHigherFramerateThan(fps);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100654 if (requested_framerate == -1) {
655 overuse_detector_->OnTargetFramerateUpdated(codec_max_framerate_);
656 return;
657 }
658 overuse_detector_->OnTargetFramerateUpdated(
659 std::min(codec_max_framerate_, requested_framerate));
660 GetAdaptCounter().DecrementFramerate(reason);
661 break;
662 }
663 case DegradationPreference::DISABLED:
664 return;
665 }
666
Henrik Boströmd2382002020-01-10 15:44:01 +0100667 // Tell the adaptation listener to reconfigure the source for us according to
668 // the latest adaptation.
669 adaptation_listener_->OnVideoSourceRestrictionsUpdated(
Henrik Boströmce0ea492020-01-13 11:27:18 +0100670 source_restrictor_->source_restrictions());
Henrik Boströmd2382002020-01-10 15:44:01 +0100671
Henrik Boströmb08882b2020-01-07 10:11:17 +0100672 last_adaptation_request_.emplace(adaptation_request);
673
674 UpdateAdaptationStats(reason);
675
676 RTC_LOG(LS_INFO) << adapt_counter.ToString();
677}
678
679bool OveruseFrameDetectorResourceAdaptationModule::AdaptDown(
680 AdaptReason reason) {
681 RTC_DCHECK(encoder_queue_);
682 RTC_DCHECK_RUN_ON(encoder_queue_);
683 AdaptationRequest adaptation_request = {
684 *last_frame_pixel_count_, encoder_stats_observer_->GetInputFrameRate(),
685 AdaptationRequest::Mode::kAdaptDown};
686
687 bool downgrade_requested =
688 last_adaptation_request_ &&
689 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
690
691 bool did_adapt = true;
692
693 switch (EffectiveDegradataionPreference()) {
694 case DegradationPreference::BALANCED:
695 break;
696 case DegradationPreference::MAINTAIN_FRAMERATE:
697 if (downgrade_requested &&
698 adaptation_request.input_pixel_count_ >=
699 last_adaptation_request_->input_pixel_count_) {
700 // Don't request lower resolution if the current resolution is not
701 // lower than the last time we asked for the resolution to be lowered.
702 return true;
703 }
704 break;
705 case DegradationPreference::MAINTAIN_RESOLUTION:
706 if (adaptation_request.framerate_fps_ <= 0 ||
707 (downgrade_requested &&
708 adaptation_request.framerate_fps_ < kMinFramerateFps)) {
709 // If no input fps estimate available, can't determine how to scale down
710 // framerate. Otherwise, don't request lower framerate if we don't have
711 // a valid frame rate. Since framerate, unlike resolution, is a measure
712 // we have to estimate, and can fluctuate naturally over time, don't
713 // make the same kind of limitations as for resolution, but trust the
714 // overuse detector to not trigger too often.
715 return true;
716 }
717 break;
718 case DegradationPreference::DISABLED:
719 return true;
720 }
721
722 switch (EffectiveDegradataionPreference()) {
723 case DegradationPreference::BALANCED: {
724 // Try scale down framerate, if lower.
725 int fps = balanced_settings_.MinFps(encoder_config_.codec_type,
726 *last_frame_pixel_count_);
Henrik Boströmce0ea492020-01-13 11:27:18 +0100727 if (source_restrictor_->RestrictFramerate(fps)) {
Henrik Boströmb08882b2020-01-07 10:11:17 +0100728 GetAdaptCounter().IncrementFramerate(reason);
729 // Check if requested fps is higher (or close to) input fps.
730 absl::optional<int> min_diff =
731 balanced_settings_.MinFpsDiff(*last_frame_pixel_count_);
732 if (min_diff && adaptation_request.framerate_fps_ > 0) {
733 int fps_diff = adaptation_request.framerate_fps_ - fps;
734 if (fps_diff < min_diff.value()) {
735 did_adapt = false;
736 }
737 }
738 break;
739 }
740 // Scale down resolution.
741 RTC_FALLTHROUGH();
742 }
743 case DegradationPreference::MAINTAIN_FRAMERATE: {
744 // Scale down resolution.
745 bool min_pixels_reached = false;
Henrik Boströmce0ea492020-01-13 11:27:18 +0100746 if (!source_restrictor_->RequestResolutionLowerThan(
Henrik Boströmb08882b2020-01-07 10:11:17 +0100747 adaptation_request.input_pixel_count_,
748 encoder_->GetEncoderInfo().scaling_settings.min_pixels_per_frame,
749 &min_pixels_reached)) {
750 if (min_pixels_reached)
751 encoder_stats_observer_->OnMinPixelLimitReached();
752 return true;
753 }
754 GetAdaptCounter().IncrementResolution(reason);
755 break;
756 }
757 case DegradationPreference::MAINTAIN_RESOLUTION: {
758 // Scale down framerate.
Henrik Boströmce0ea492020-01-13 11:27:18 +0100759 const int requested_framerate =
760 source_restrictor_->RequestFramerateLowerThan(
761 adaptation_request.framerate_fps_);
Henrik Boströmb08882b2020-01-07 10:11:17 +0100762 if (requested_framerate == -1)
763 return true;
764 RTC_DCHECK_NE(codec_max_framerate_, -1);
765 overuse_detector_->OnTargetFramerateUpdated(
766 std::min(codec_max_framerate_, requested_framerate));
767 GetAdaptCounter().IncrementFramerate(reason);
768 break;
769 }
770 case DegradationPreference::DISABLED:
771 RTC_NOTREACHED();
772 }
773
Henrik Boströmd2382002020-01-10 15:44:01 +0100774 // Tell the adaptation listener to reconfigure the source for us according to
775 // the latest adaptation.
776 adaptation_listener_->OnVideoSourceRestrictionsUpdated(
Henrik Boströmce0ea492020-01-13 11:27:18 +0100777 source_restrictor_->source_restrictions());
Henrik Boströmd2382002020-01-10 15:44:01 +0100778
Henrik Boströmb08882b2020-01-07 10:11:17 +0100779 last_adaptation_request_.emplace(adaptation_request);
780
781 UpdateAdaptationStats(reason);
782
783 RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString();
784 return did_adapt;
785}
786
787// TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged.
788void OveruseFrameDetectorResourceAdaptationModule::UpdateAdaptationStats(
789 AdaptReason reason) {
790 switch (reason) {
791 case kCpu:
792 encoder_stats_observer_->OnAdaptationChanged(
793 VideoStreamEncoderObserver::AdaptationReason::kCpu,
794 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
795 break;
796 case kQuality:
797 encoder_stats_observer_->OnAdaptationChanged(
798 VideoStreamEncoderObserver::AdaptationReason::kQuality,
799 GetActiveCounts(kCpu), GetActiveCounts(kQuality));
800 break;
801 }
802}
803
804VideoStreamEncoderObserver::AdaptationSteps
805OveruseFrameDetectorResourceAdaptationModule::GetActiveCounts(
806 AdaptReason reason) {
807 RTC_DCHECK(encoder_queue_);
808 RTC_DCHECK_RUN_ON(encoder_queue_);
809 VideoStreamEncoderObserver::AdaptationSteps counts =
810 GetConstAdaptCounter().Counts(reason);
811 switch (reason) {
812 case kCpu:
813 if (!IsFramerateScalingEnabled(degradation_preference_))
814 counts.num_framerate_reductions = absl::nullopt;
815 if (!IsResolutionScalingEnabled(degradation_preference_))
816 counts.num_resolution_reductions = absl::nullopt;
817 break;
818 case kQuality:
819 if (!IsFramerateScalingEnabled(degradation_preference_) ||
820 !is_quality_scaler_enabled_) {
821 counts.num_framerate_reductions = absl::nullopt;
822 }
823 if (!IsResolutionScalingEnabled(degradation_preference_) ||
824 !is_quality_scaler_enabled_) {
825 counts.num_resolution_reductions = absl::nullopt;
826 }
827 break;
828 }
829 return counts;
830}
831
832DegradationPreference OveruseFrameDetectorResourceAdaptationModule::
833 EffectiveDegradataionPreference() {
834 // Balanced mode for screenshare works via automatic animation detection:
835 // Resolution is capped for fullscreen animated content.
836 // Adapatation is done only via framerate downgrade.
837 // Thus effective degradation preference is MAINTAIN_RESOLUTION.
838 return (encoder_config_.content_type ==
839 VideoEncoderConfig::ContentType::kScreen &&
840 degradation_preference_ == DegradationPreference::BALANCED)
841 ? DegradationPreference::MAINTAIN_RESOLUTION
842 : degradation_preference_;
843}
844
845OveruseFrameDetectorResourceAdaptationModule::AdaptCounter&
846OveruseFrameDetectorResourceAdaptationModule::GetAdaptCounter() {
847 return adapt_counters_[degradation_preference_];
848}
849
850const OveruseFrameDetectorResourceAdaptationModule::AdaptCounter&
851OveruseFrameDetectorResourceAdaptationModule::GetConstAdaptCounter() {
852 RTC_DCHECK(encoder_queue_);
853 RTC_DCHECK_RUN_ON(encoder_queue_);
854 return adapt_counters_[degradation_preference_];
855}
856
857absl::optional<VideoEncoder::QpThresholds>
858OveruseFrameDetectorResourceAdaptationModule::GetQpThresholds() const {
859 RTC_DCHECK(encoder_queue_);
860 RTC_DCHECK_RUN_ON(encoder_queue_);
861 RTC_DCHECK(last_frame_pixel_count_.has_value());
862 return balanced_settings_.GetQpThresholds(encoder_config_.codec_type,
863 last_frame_pixel_count_.value());
864}
865
866bool OveruseFrameDetectorResourceAdaptationModule::CanAdaptUpResolution(
867 int pixels,
868 uint32_t bitrate_bps) const {
869 absl::optional<VideoEncoder::ResolutionBitrateLimits> bitrate_limits =
Henrik Boströmce0ea492020-01-13 11:27:18 +0100870 GetEncoderBitrateLimits(
871 encoder_->GetEncoderInfo(),
872 source_restrictor_->GetHigherResolutionThan(pixels));
Henrik Boströmb08882b2020-01-07 10:11:17 +0100873 if (!bitrate_limits.has_value() || bitrate_bps == 0) {
874 return true; // No limit configured or bitrate provided.
875 }
876 RTC_DCHECK_GE(bitrate_limits->frame_size_pixels, pixels);
877 return bitrate_bps >=
878 static_cast<uint32_t>(bitrate_limits->min_start_bitrate_bps);
879}
880
881} // namespace webrtc