blob: eccf0f2e2b384ab2c8e1c7d7077566f530053058 [file] [log] [blame]
Henrik Boströmb08882b2020-01-07 09:11:171/*
Henrik Boström62057622020-03-10 18:08:052 * Copyright 2020 The WebRTC Project Authors. All rights reserved.
Henrik Boströmb08882b2020-01-07 09:11:173 *
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
Henrik Boström62057622020-03-10 18:08:0511#include "video/adaptation/resource_adaptation_processor.h"
Henrik Boströmb08882b2020-01-07 09:11:1712
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öm48258ac2020-02-06 11:49:5723#include "call/adaptation/resource.h"
Henrik Boströmce0ea492020-01-13 10:27:1824#include "call/adaptation/video_source_restrictions.h"
Henrik Boströmb08882b2020-01-07 09:11:1725#include "rtc_base/logging.h"
Henrik Boströmd2382002020-01-10 14:44:0126#include "rtc_base/numerics/safe_conversions.h"
Henrik Boströmb08882b2020-01-07 09:11:1727#include "rtc_base/strings/string_builder.h"
Henrik Boströmad515a22020-01-27 12:38:0528#include "rtc_base/time_utils.h"
Henrik Boströmb08882b2020-01-07 09:11:1729
30namespace webrtc {
31
Henrik Boström62057622020-03-10 18:08:0532const int kDefaultInputPixelsWidth = 176;
33const int kDefaultInputPixelsHeight = 144;
34
Henrik Boströmb08882b2020-01-07 09:11:1735namespace {
36
Henrik Boströmb08882b2020-01-07 09:11:1737bool IsResolutionScalingEnabled(DegradationPreference degradation_preference) {
38 return degradation_preference == DegradationPreference::MAINTAIN_FRAMERATE ||
39 degradation_preference == DegradationPreference::BALANCED;
40}
41
42bool IsFramerateScalingEnabled(DegradationPreference degradation_preference) {
43 return degradation_preference == DegradationPreference::MAINTAIN_RESOLUTION ||
44 degradation_preference == DegradationPreference::BALANCED;
45}
46
Henrik Boström8234b922020-01-13 16:26:5047// Returns modified restrictions where any constraints that don't apply to the
48// degradation preference are cleared.
49VideoSourceRestrictions ApplyDegradationPreference(
50 VideoSourceRestrictions source_restrictions,
51 DegradationPreference degradation_preference) {
52 switch (degradation_preference) {
53 case DegradationPreference::BALANCED:
54 break;
55 case DegradationPreference::MAINTAIN_FRAMERATE:
56 source_restrictions.set_max_frame_rate(absl::nullopt);
57 break;
58 case DegradationPreference::MAINTAIN_RESOLUTION:
59 source_restrictions.set_max_pixels_per_frame(absl::nullopt);
60 source_restrictions.set_target_pixels_per_frame(absl::nullopt);
61 break;
62 case DegradationPreference::DISABLED:
63 source_restrictions.set_max_pixels_per_frame(absl::nullopt);
64 source_restrictions.set_target_pixels_per_frame(absl::nullopt);
65 source_restrictions.set_max_frame_rate(absl::nullopt);
66 }
67 return source_restrictions;
68}
69
Evan Shrubsole33be9df2020-03-05 17:39:3270// Returns AdaptationCounters where constraints that don't apply to the
Henrik Boströmefbec9a2020-03-06 09:41:2571// degredation preference are cleared. This behaviour must reflect that of
72// ApplyDegredationPreference for SourceRestrictions. Any to that method must
73// also change this one.
Evan Shrubsole33be9df2020-03-05 17:39:3274AdaptationCounters ApplyDegradationPreference(
75 AdaptationCounters counters,
76 DegradationPreference degradation_preference) {
77 switch (degradation_preference) {
78 case DegradationPreference::BALANCED:
79 break;
80 case DegradationPreference::MAINTAIN_FRAMERATE:
81 counters.fps_adaptations = 0;
82 break;
83 case DegradationPreference::MAINTAIN_RESOLUTION:
84 counters.resolution_adaptations = 0;
85 break;
86 case DegradationPreference::DISABLED:
87 counters.resolution_adaptations = 0;
88 counters.fps_adaptations = 0;
89 break;
90 default:
91 RTC_NOTREACHED();
92 }
93 return counters;
94}
95
Henrik Boströmb08882b2020-01-07 09:11:1796} // namespace
97
Henrik Boström62057622020-03-10 18:08:0598class ResourceAdaptationProcessor::InitialFrameDropper {
Henrik Boström8cfecac2020-02-07 10:29:1499 public:
100 explicit InitialFrameDropper(QualityScalerResource* quality_scaler_resource)
101 : quality_scaler_resource_(quality_scaler_resource),
102 quality_scaler_settings_(QualityScalerSettings::ParseFromFieldTrials()),
103 has_seen_first_bwe_drop_(false),
104 set_start_bitrate_(DataRate::Zero()),
105 set_start_bitrate_time_ms_(0),
106 initial_framedrop_(0) {
107 RTC_DCHECK(quality_scaler_resource_);
108 }
109
110 // Output signal.
111 bool DropInitialFrames() const {
112 return initial_framedrop_ < kMaxInitialFramedrop;
113 }
114
115 // Input signals.
116 void SetStartBitrate(DataRate start_bitrate, int64_t now_ms) {
117 set_start_bitrate_ = start_bitrate;
118 set_start_bitrate_time_ms_ = now_ms;
119 }
120
121 void SetTargetBitrate(DataRate target_bitrate, int64_t now_ms) {
122 if (set_start_bitrate_ > DataRate::Zero() && !has_seen_first_bwe_drop_ &&
123 quality_scaler_resource_->is_started() &&
124 quality_scaler_settings_.InitialBitrateIntervalMs() &&
125 quality_scaler_settings_.InitialBitrateFactor()) {
126 int64_t diff_ms = now_ms - set_start_bitrate_time_ms_;
127 if (diff_ms <
128 quality_scaler_settings_.InitialBitrateIntervalMs().value() &&
129 (target_bitrate <
130 (set_start_bitrate_ *
131 quality_scaler_settings_.InitialBitrateFactor().value()))) {
132 RTC_LOG(LS_INFO) << "Reset initial_framedrop_. Start bitrate: "
133 << set_start_bitrate_.bps()
134 << ", target bitrate: " << target_bitrate.bps();
135 initial_framedrop_ = 0;
136 has_seen_first_bwe_drop_ = true;
137 }
138 }
139 }
140
141 void OnFrameDroppedDueToSize() { ++initial_framedrop_; }
142
Mirko Bonadei2e161c42020-02-20 08:45:01143 void OnMaybeEncodeFrame() { initial_framedrop_ = kMaxInitialFramedrop; }
Henrik Boström8cfecac2020-02-07 10:29:14144
145 void OnQualityScalerSettingsUpdated() {
146 if (quality_scaler_resource_->is_started()) {
147 // Restart frame drops due to size.
148 initial_framedrop_ = 0;
149 } else {
150 // Quality scaling disabled so we shouldn't drop initial frames.
151 initial_framedrop_ = kMaxInitialFramedrop;
152 }
153 }
154
155 private:
156 // The maximum number of frames to drop at beginning of stream to try and
157 // achieve desired bitrate.
158 static const int kMaxInitialFramedrop = 4;
159
160 const QualityScalerResource* quality_scaler_resource_;
161 const QualityScalerSettings quality_scaler_settings_;
162 bool has_seen_first_bwe_drop_;
163 DataRate set_start_bitrate_;
164 int64_t set_start_bitrate_time_ms_;
165 // Counts how many frames we've dropped in the initial framedrop phase.
166 int initial_framedrop_;
167};
168
Henrik Boström62057622020-03-10 18:08:05169ResourceAdaptationProcessor::ResourceAdaptationProcessor(
170 Clock* clock,
171 bool experiment_cpu_load_estimator,
172 std::unique_ptr<OveruseFrameDetector> overuse_detector,
173 VideoStreamEncoderObserver* encoder_stats_observer,
174 ResourceAdaptationProcessorListener* adaptation_listener)
Henrik Boström07b17df2020-01-15 10:42:12175 : adaptation_listener_(adaptation_listener),
Evan Shrubsole7c3a1fc2020-02-04 15:26:38176 clock_(clock),
Evan Shrubsoleaa6fbc12020-02-25 15:26:01177 state_(State::kStopped),
Henrik Boströmad515a22020-01-27 12:38:05178 experiment_cpu_load_estimator_(experiment_cpu_load_estimator),
Henrik Boströma3d42522020-01-16 12:55:29179 has_input_video_(false),
Henrik Boströmb08882b2020-01-07 09:11:17180 degradation_preference_(DegradationPreference::DISABLED),
Henrik Boströmefbec9a2020-03-06 09:41:25181 stream_adapter_(std::make_unique<VideoStreamAdapter>()),
Henrik Boström7875c992020-02-06 09:35:00182 encode_usage_resource_(
Henrik Boström48258ac2020-02-06 11:49:57183 std::make_unique<EncodeUsageResource>(std::move(overuse_detector))),
184 quality_scaler_resource_(std::make_unique<QualityScalerResource>()),
Henrik Boström8cfecac2020-02-07 10:29:14185 initial_frame_dropper_(std::make_unique<InitialFrameDropper>(
186 quality_scaler_resource_.get())),
Henrik Boström7875c992020-02-06 09:35:00187 quality_scaling_experiment_enabled_(QualityScalingExperiment::Enabled()),
Henrik Boströmd4578ae2020-01-22 15:16:04188 last_input_frame_size_(absl::nullopt),
Henrik Boströmfae6f0e2020-01-20 10:16:50189 target_frame_rate_(absl::nullopt),
Evan Shrubsole7c3a1fc2020-02-04 15:26:38190 encoder_target_bitrate_bps_(absl::nullopt),
Evan Shrubsolee331a122020-02-05 12:30:33191 quality_rampup_done_(false),
192 quality_rampup_experiment_(QualityRampupExperiment::ParseSettings()),
Henrik Boström4bab2fc2020-01-21 10:18:06193 encoder_settings_(absl::nullopt),
Evan Shrubsole33be9df2020-03-05 17:39:32194 encoder_stats_observer_(encoder_stats_observer),
195 active_counts_() {
Henrik Boströmd2382002020-01-10 14:44:01196 RTC_DCHECK(adaptation_listener_);
Henrik Boströmb08882b2020-01-07 09:11:17197 RTC_DCHECK(encoder_stats_observer_);
Evan Shrubsoleaa6fbc12020-02-25 15:26:01198 AddResource(encode_usage_resource_.get(),
199 AdaptationObserverInterface::AdaptReason::kCpu);
200 AddResource(quality_scaler_resource_.get(),
201 AdaptationObserverInterface::AdaptReason::kQuality);
Henrik Boströmb08882b2020-01-07 09:11:17202}
203
Henrik Boström62057622020-03-10 18:08:05204ResourceAdaptationProcessor::~ResourceAdaptationProcessor() {
Evan Shrubsoleaa6fbc12020-02-25 15:26:01205 RTC_DCHECK_EQ(state_, State::kStopped);
206}
Henrik Boströmb08882b2020-01-07 09:11:17207
Henrik Boström62057622020-03-10 18:08:05208void ResourceAdaptationProcessor::StartResourceAdaptation(
209 ResourceAdaptationProcessorListener* adaptation_listener) {
Evan Shrubsoleaa6fbc12020-02-25 15:26:01210 RTC_DCHECK_EQ(state_, State::kStopped);
Henrik Boström4bab2fc2020-01-21 10:18:06211 RTC_DCHECK(encoder_settings_.has_value());
Henrik Boström7875c992020-02-06 09:35:00212 // TODO(https://crbug.com/webrtc/11222): Rethink when the adaptation listener
213 // should be passed in and why. If resources are separated from modules then
214 // those resources may be started or stopped separately from the module.
Henrik Boströmd2382002020-01-10 14:44:01215 RTC_DCHECK_EQ(adaptation_listener, adaptation_listener_);
Henrik Boström7875c992020-02-06 09:35:00216 encode_usage_resource_->StartCheckForOveruse(GetCpuOveruseOptions());
Evan Shrubsoleaa6fbc12020-02-25 15:26:01217 for (auto& resource_and_reason : resources_) {
218 resource_and_reason.resource->RegisterListener(this);
219 }
220 state_ = State::kStarted;
Henrik Boströmb08882b2020-01-07 09:11:17221}
222
Henrik Boström62057622020-03-10 18:08:05223void ResourceAdaptationProcessor::StopResourceAdaptation() {
Henrik Boström7875c992020-02-06 09:35:00224 encode_usage_resource_->StopCheckForOveruse();
225 quality_scaler_resource_->StopCheckForOveruse();
Evan Shrubsoleaa6fbc12020-02-25 15:26:01226 for (auto& resource_and_reason : resources_) {
227 resource_and_reason.resource->UnregisterListener(this);
228 }
229 state_ = State::kStopped;
230}
231
Henrik Boström62057622020-03-10 18:08:05232void ResourceAdaptationProcessor::AddResource(Resource* resource) {
Evan Shrubsoleaa6fbc12020-02-25 15:26:01233 return AddResource(resource, AdaptationObserverInterface::AdaptReason::kCpu);
234}
235
Henrik Boström62057622020-03-10 18:08:05236void ResourceAdaptationProcessor::AddResource(
Evan Shrubsoleaa6fbc12020-02-25 15:26:01237 Resource* resource,
238 AdaptationObserverInterface::AdaptReason reason) {
239 RTC_DCHECK(resource);
240 RTC_DCHECK(absl::c_find_if(resources_,
241 [resource](const ResourceAndReason& r) {
242 return r.resource == resource;
243 }) == resources_.end())
244 << "Resource " << resource->name() << " already was inserted";
245 resources_.emplace_back(resource, reason);
Henrik Boströmb08882b2020-01-07 09:11:17246}
247
Henrik Boström62057622020-03-10 18:08:05248void ResourceAdaptationProcessor::SetHasInputVideo(bool has_input_video) {
Henrik Boström7875c992020-02-06 09:35:00249 // While false, OnResourceUnderuse() and OnResourceOveruse() are NO-OPS.
Henrik Boströma3d42522020-01-16 12:55:29250 has_input_video_ = has_input_video;
251}
252
Henrik Boström62057622020-03-10 18:08:05253void ResourceAdaptationProcessor::SetDegradationPreference(
Henrik Boströma3d42522020-01-16 12:55:29254 DegradationPreference degradation_preference) {
Henrik Boströma3d42522020-01-16 12:55:29255 degradation_preference_ = degradation_preference;
Henrik Boströmb0f2e0c2020-03-06 12:32:03256 if (stream_adapter_->SetDegradationPreference(degradation_preference) ==
257 VideoStreamAdapter::SetDegradationPreferenceResult::
258 kRestrictionsCleared) {
259 active_counts_.fill(AdaptationCounters());
260 }
Henrik Boströma3d42522020-01-16 12:55:29261 MaybeUpdateVideoSourceRestrictions();
262}
263
Henrik Boström62057622020-03-10 18:08:05264void ResourceAdaptationProcessor::SetEncoderSettings(
Henrik Boström4bab2fc2020-01-21 10:18:06265 EncoderSettings encoder_settings) {
266 encoder_settings_ = std::move(encoder_settings);
Evan Shrubsolee331a122020-02-05 12:30:33267
268 quality_rampup_experiment_.SetMaxBitrate(
269 LastInputFrameSizeOrDefault(),
270 encoder_settings_->video_codec().maxBitrate);
Henrik Boström4bab2fc2020-01-21 10:18:06271 MaybeUpdateTargetFrameRate();
272}
273
Henrik Boström62057622020-03-10 18:08:05274void ResourceAdaptationProcessor::SetStartBitrate(DataRate start_bitrate) {
Evan Shrubsole7c3a1fc2020-02-04 15:26:38275 if (!start_bitrate.IsZero())
276 encoder_target_bitrate_bps_ = start_bitrate.bps();
Henrik Boström8cfecac2020-02-07 10:29:14277 initial_frame_dropper_->SetStartBitrate(start_bitrate,
278 clock_->TimeInMicroseconds());
Evan Shrubsole7c3a1fc2020-02-04 15:26:38279}
280
Henrik Boström62057622020-03-10 18:08:05281void ResourceAdaptationProcessor::SetTargetBitrate(DataRate target_bitrate) {
Evan Shrubsole7c3a1fc2020-02-04 15:26:38282 if (!target_bitrate.IsZero())
283 encoder_target_bitrate_bps_ = target_bitrate.bps();
Henrik Boström8cfecac2020-02-07 10:29:14284 initial_frame_dropper_->SetTargetBitrate(target_bitrate,
285 clock_->TimeInMilliseconds());
Henrik Boströmede69c02020-01-21 16:45:35286}
287
Henrik Boström62057622020-03-10 18:08:05288void ResourceAdaptationProcessor::SetEncoderRates(
Evan Shrubsolee331a122020-02-05 12:30:33289 const VideoEncoder::RateControlParameters& encoder_rates) {
290 encoder_rates_ = encoder_rates;
291}
292
Henrik Boström62057622020-03-10 18:08:05293void ResourceAdaptationProcessor::ResetVideoSourceRestrictions() {
Henrik Boströmefbec9a2020-03-06 09:41:25294 stream_adapter_->ClearRestrictions();
Evan Shrubsole33be9df2020-03-05 17:39:32295 active_counts_.fill(AdaptationCounters());
Henrik Boströmfae6f0e2020-01-20 10:16:50296 MaybeUpdateVideoSourceRestrictions();
297}
298
Henrik Boström62057622020-03-10 18:08:05299void ResourceAdaptationProcessor::OnFrame(const VideoFrame& frame) {
Henrik Boströmd4578ae2020-01-22 15:16:04300 last_input_frame_size_ = frame.size();
Henrik Boströmb08882b2020-01-07 09:11:17301}
302
Henrik Boström62057622020-03-10 18:08:05303void ResourceAdaptationProcessor::OnFrameDroppedDueToSize() {
Henrik Boströmefbec9a2020-03-06 09:41:25304 AdaptationCounters counters_before = stream_adapter_->adaptation_counters();
Henrik Boström7875c992020-02-06 09:35:00305 OnResourceOveruse(AdaptationObserverInterface::AdaptReason::kQuality);
Evan Shrubsole6c13fd92020-01-21 08:57:54306 if (degradation_preference() == DegradationPreference::BALANCED &&
Henrik Boströmefbec9a2020-03-06 09:41:25307 stream_adapter_->adaptation_counters().fps_adaptations >
Evan Shrubsole33be9df2020-03-05 17:39:32308 counters_before.fps_adaptations) {
Evan Shrubsole6c13fd92020-01-21 08:57:54309 // Adapt framerate in same step as resolution.
Henrik Boström7875c992020-02-06 09:35:00310 OnResourceOveruse(AdaptationObserverInterface::AdaptReason::kQuality);
Evan Shrubsole6c13fd92020-01-21 08:57:54311 }
Henrik Boströmefbec9a2020-03-06 09:41:25312 if (stream_adapter_->adaptation_counters().resolution_adaptations >
Evan Shrubsole33be9df2020-03-05 17:39:32313 counters_before.resolution_adaptations) {
Evan Shrubsole6c13fd92020-01-21 08:57:54314 encoder_stats_observer_->OnInitialQualityResolutionAdaptDown();
315 }
Henrik Boström8cfecac2020-02-07 10:29:14316 initial_frame_dropper_->OnFrameDroppedDueToSize();
Evan Shrubsole6c13fd92020-01-21 08:57:54317}
318
Henrik Boström62057622020-03-10 18:08:05319void ResourceAdaptationProcessor::OnEncodeStarted(
Henrik Boströmd4578ae2020-01-22 15:16:04320 const VideoFrame& cropped_frame,
321 int64_t time_when_first_seen_us) {
Henrik Boström7875c992020-02-06 09:35:00322 encode_usage_resource_->OnEncodeStarted(cropped_frame,
323 time_when_first_seen_us);
Henrik Boströmd4578ae2020-01-22 15:16:04324}
325
Henrik Boström62057622020-03-10 18:08:05326void ResourceAdaptationProcessor::OnEncodeCompleted(
Evan Shrubsolebfe3ef82020-01-30 13:29:35327 const EncodedImage& encoded_image,
Henrik Boströmd4578ae2020-01-22 15:16:04328 int64_t time_sent_in_us,
Henrik Boströmd4578ae2020-01-22 15:16:04329 absl::optional<int> encode_duration_us) {
Henrik Boström7875c992020-02-06 09:35:00330 // Inform |encode_usage_resource_| of the encode completed event.
Evan Shrubsolebfe3ef82020-01-30 13:29:35331 uint32_t timestamp = encoded_image.Timestamp();
332 int64_t capture_time_us =
333 encoded_image.capture_time_ms_ * rtc::kNumMicrosecsPerMillisec;
Henrik Boström7875c992020-02-06 09:35:00334 encode_usage_resource_->OnEncodeCompleted(
335 timestamp, time_sent_in_us, capture_time_us, encode_duration_us);
336 // Inform |quality_scaler_resource_| of the encode completed event.
337 quality_scaler_resource_->OnEncodeCompleted(encoded_image, time_sent_in_us);
Henrik Boströmb08882b2020-01-07 09:11:17338}
339
Henrik Boström62057622020-03-10 18:08:05340void ResourceAdaptationProcessor::OnFrameDropped(
Evan Shrubsolec809e8b2020-01-31 14:36:35341 EncodedImageCallback::DropReason reason) {
Henrik Boström7875c992020-02-06 09:35:00342 quality_scaler_resource_->OnFrameDropped(reason);
Evan Shrubsolec809e8b2020-01-31 14:36:35343}
344
Henrik Boström62057622020-03-10 18:08:05345bool ResourceAdaptationProcessor::DropInitialFrames() const {
Henrik Boström8cfecac2020-02-07 10:29:14346 return initial_frame_dropper_->DropInitialFrames();
Evan Shrubsolef2be3ef2020-02-03 09:43:31347}
348
Henrik Boström62057622020-03-10 18:08:05349void ResourceAdaptationProcessor::OnMaybeEncodeFrame() {
Mirko Bonadei2e161c42020-02-20 08:45:01350 initial_frame_dropper_->OnMaybeEncodeFrame();
351 MaybePerformQualityRampupExperiment();
352}
353
Henrik Boström62057622020-03-10 18:08:05354void ResourceAdaptationProcessor::UpdateQualityScalerSettings(
Evan Shrubsolecf059522020-01-29 16:04:44355 absl::optional<VideoEncoder::QpThresholds> qp_thresholds) {
356 if (qp_thresholds.has_value()) {
Henrik Boström7875c992020-02-06 09:35:00357 quality_scaler_resource_->StopCheckForOveruse();
358 quality_scaler_resource_->StartCheckForOveruse(qp_thresholds.value());
Evan Shrubsolecf059522020-01-29 16:04:44359 } else {
Henrik Boström7875c992020-02-06 09:35:00360 quality_scaler_resource_->StopCheckForOveruse();
Evan Shrubsolecf059522020-01-29 16:04:44361 }
Henrik Boström8cfecac2020-02-07 10:29:14362 initial_frame_dropper_->OnQualityScalerSettingsUpdated();
Henrik Boströmb08882b2020-01-07 09:11:17363}
364
Henrik Boström62057622020-03-10 18:08:05365void ResourceAdaptationProcessor::ConfigureQualityScaler(
Evan Shrubsolec81798b2020-02-03 14:46:08366 const VideoEncoder::EncoderInfo& encoder_info) {
367 const auto scaling_settings = encoder_info.scaling_settings;
368 const bool quality_scaling_allowed =
369 IsResolutionScalingEnabled(degradation_preference_) &&
370 scaling_settings.thresholds;
371
Henrik Boström7875c992020-02-06 09:35:00372 // TODO(https://crbug.com/webrtc/11222): Should this move to
373 // QualityScalerResource?
Evan Shrubsolec81798b2020-02-03 14:46:08374 if (quality_scaling_allowed) {
Henrik Boström7875c992020-02-06 09:35:00375 if (!quality_scaler_resource_->is_started()) {
Evan Shrubsolec81798b2020-02-03 14:46:08376 // Quality scaler has not already been configured.
377
378 // Use experimental thresholds if available.
379 absl::optional<VideoEncoder::QpThresholds> experimental_thresholds;
380 if (quality_scaling_experiment_enabled_) {
381 experimental_thresholds = QualityScalingExperiment::GetQpThresholds(
Henrik Boströmb0f2e0c2020-03-06 12:32:03382 GetVideoCodecTypeOrGeneric(encoder_settings_));
Evan Shrubsolec81798b2020-02-03 14:46:08383 }
384 UpdateQualityScalerSettings(experimental_thresholds
385 ? *experimental_thresholds
386 : *(scaling_settings.thresholds));
387 }
388 } else {
389 UpdateQualityScalerSettings(absl::nullopt);
390 }
391
392 // Set the qp-thresholds to the balanced settings if balanced mode.
393 if (degradation_preference_ == DegradationPreference::BALANCED &&
Henrik Boström7875c992020-02-06 09:35:00394 quality_scaler_resource_->is_started()) {
Evan Shrubsolec81798b2020-02-03 14:46:08395 absl::optional<VideoEncoder::QpThresholds> thresholds =
Henrik Boströmb0f2e0c2020-03-06 12:32:03396 stream_adapter_->balanced_settings().GetQpThresholds(
397 GetVideoCodecTypeOrGeneric(encoder_settings_),
398 LastInputFrameSizeOrDefault());
Evan Shrubsolec81798b2020-02-03 14:46:08399 if (thresholds) {
Henrik Boström7875c992020-02-06 09:35:00400 quality_scaler_resource_->SetQpThresholds(*thresholds);
Evan Shrubsolec81798b2020-02-03 14:46:08401 }
402 }
403
404 encoder_stats_observer_->OnAdaptationChanged(
405 VideoStreamEncoderObserver::AdaptationReason::kNone,
406 GetActiveCounts(AdaptationObserverInterface::AdaptReason::kCpu),
407 GetActiveCounts(AdaptationObserverInterface::AdaptReason::kQuality));
408}
409
Henrik Boström48258ac2020-02-06 11:49:57410ResourceListenerResponse
Henrik Boström62057622020-03-10 18:08:05411ResourceAdaptationProcessor::OnResourceUsageStateMeasured(
Henrik Boström48258ac2020-02-06 11:49:57412 const Resource& resource) {
Evan Shrubsoleaa6fbc12020-02-25 15:26:01413 const auto& registered_resource =
414 absl::c_find_if(resources_, [&resource](const ResourceAndReason& r) {
415 return r.resource == &resource;
416 });
417 RTC_DCHECK(registered_resource != resources_.end())
418 << resource.name() << " not found.";
419
420 const AdaptationObserverInterface::AdaptReason reason =
421 registered_resource->reason;
Henrik Boström48258ac2020-02-06 11:49:57422 switch (resource.usage_state()) {
423 case ResourceUsageState::kOveruse:
424 return OnResourceOveruse(reason);
425 case ResourceUsageState::kStable:
426 // Do nothing.
427 //
428 // This module has two resources: |encoude_usage_resource_| and
429 // |quality_scaler_resource_|. A smarter adaptation module might not
430 // attempt to adapt up unless ALL resources were underused, but this
431 // module acts on each resource's measurement in isolation - without
432 // taking the current usage of any other resource into account.
433 return ResourceListenerResponse::kNothing;
434 case ResourceUsageState::kUnderuse:
435 OnResourceUnderuse(reason);
436 return ResourceListenerResponse::kNothing;
437 }
438}
439
Henrik Boström62057622020-03-10 18:08:05440void ResourceAdaptationProcessor::OnResourceUnderuse(
Henrik Boström8d9f7502020-03-06 13:51:34441 AdaptationObserverInterface::AdaptReason reason) {
Henrik Boströmb0f2e0c2020-03-06 12:32:03442 // We can't adapt up if we're already at the highest setting.
Evan Shrubsole33be9df2020-03-05 17:39:32443 // Note that this only includes counts relevant to the current degradation
444 // preference. e.g. we previously adapted resolution, now prefer adpating fps,
445 // only count the fps adaptations and not the previous resolution adaptations.
Henrik Boströmb0f2e0c2020-03-06 12:32:03446 //
Evan Shrubsole33be9df2020-03-05 17:39:32447 // TODO(https://crbug.com/webrtc/11394): Checking the counts for reason should
Henrik Boströmb0f2e0c2020-03-06 12:32:03448 // be replaced with checking the overuse state of all resources. This is
449 // effectively trying to infer if the the Resource specified by |reason| is OK
450 // with adapting up by looking at active counters. If the relevant Resources
451 // simply told us this directly we wouldn't have to depend on stats counters
452 // to abort GetAdaptUpTarget().
Evan Shrubsole33be9df2020-03-05 17:39:32453 int num_downgrades = ApplyDegradationPreference(active_counts_[reason],
454 degradation_preference_)
455 .Total();
Henrik Boströmd2a1f09b2020-02-25 08:46:36456 RTC_DCHECK_GE(num_downgrades, 0);
457 if (num_downgrades == 0)
Henrik Boström8d9f7502020-03-06 13:51:34458 return;
459 // Current video input states used by VideoStreamAdapter.
460 const VideoStreamAdapter::VideoInputMode input_mode = GetVideoInputMode();
461 const int input_pixels = LastInputFrameSizeOrDefault();
462 const int input_fps = encoder_stats_observer_->GetInputFrameRate();
Henrik Boström60383832020-02-28 08:03:53463 // Should we adapt, if so to what target?
Henrik Boström34531492020-03-12 11:41:19464 VideoStreamAdapter::AdaptationTargetOrReason target_or_reason =
Henrik Boström8d9f7502020-03-06 13:51:34465 stream_adapter_->GetAdaptUpTarget(encoder_settings_,
466 encoder_target_bitrate_bps_, input_mode,
467 input_pixels, input_fps, reason);
Henrik Boström34531492020-03-12 11:41:19468 if (!target_or_reason.has_target())
Henrik Boström60383832020-02-28 08:03:53469 return;
470 // Apply target.
Henrik Boström34531492020-03-12 11:41:19471 stream_adapter_->ApplyAdaptationTarget(target_or_reason.target(),
472 encoder_settings_, input_mode,
473 input_pixels, input_fps);
Henrik Boström60383832020-02-28 08:03:53474 // Update VideoSourceRestrictions based on adaptation. This also informs the
475 // |adaptation_listener_|.
476 MaybeUpdateVideoSourceRestrictions();
477 // Stats and logging.
478 UpdateAdaptationStats(reason);
Evan Shrubsole33be9df2020-03-05 17:39:32479 RTC_LOG(LS_INFO) << ActiveCountsToString();
Henrik Boströmb08882b2020-01-07 09:11:17480}
481
Henrik Boström62057622020-03-10 18:08:05482ResourceListenerResponse ResourceAdaptationProcessor::OnResourceOveruse(
Henrik Boström7875c992020-02-06 09:35:00483 AdaptationObserverInterface::AdaptReason reason) {
Henrik Boströma3d42522020-01-16 12:55:29484 if (!has_input_video_)
Henrik Boström48258ac2020-02-06 11:49:57485 return ResourceListenerResponse::kQualityScalerShouldIncreaseFrequency;
Henrik Boström8d9f7502020-03-06 13:51:34486 // Current video input states used by VideoStreamAdapter.
487 const VideoStreamAdapter::VideoInputMode input_mode = GetVideoInputMode();
488 const int input_pixels = LastInputFrameSizeOrDefault();
489 const int input_fps = encoder_stats_observer_->GetInputFrameRate();
Henrik Boström60383832020-02-28 08:03:53490 // Should we adapt, if so to what target?
Henrik Boström34531492020-03-12 11:41:19491 VideoStreamAdapter::AdaptationTargetOrReason target_or_reason =
Henrik Boström8d9f7502020-03-06 13:51:34492 stream_adapter_->GetAdaptDownTarget(encoder_settings_, input_mode,
Henrik Boström34531492020-03-12 11:41:19493 input_pixels, input_fps);
494 if (target_or_reason.min_pixel_limit_reached())
495 encoder_stats_observer_->OnMinPixelLimitReached();
496 if (!target_or_reason.has_target())
Henrik Boströmd2a1f09b2020-02-25 08:46:36497 return ResourceListenerResponse::kNothing;
Henrik Boström60383832020-02-28 08:03:53498 // Apply target.
Henrik Boström8d9f7502020-03-06 13:51:34499 ResourceListenerResponse response = stream_adapter_->ApplyAdaptationTarget(
Henrik Boström34531492020-03-12 11:41:19500 target_or_reason.target(), encoder_settings_, input_mode, input_pixels,
501 input_fps);
Henrik Boström60383832020-02-28 08:03:53502 // Update VideoSourceRestrictions based on adaptation. This also informs the
503 // |adaptation_listener_|.
Henrik Boström07b17df2020-01-15 10:42:12504 MaybeUpdateVideoSourceRestrictions();
Henrik Boström60383832020-02-28 08:03:53505 // Stats and logging.
Henrik Boströmb08882b2020-01-07 09:11:17506 UpdateAdaptationStats(reason);
Evan Shrubsole33be9df2020-03-05 17:39:32507 RTC_LOG(INFO) << ActiveCountsToString();
Henrik Boström8d9f7502020-03-06 13:51:34508 return response;
Henrik Boströmb08882b2020-01-07 09:11:17509}
510
Henrik Boströmad515a22020-01-27 12:38:05511// TODO(pbos): Lower these thresholds (to closer to 100%) when we handle
512// pipelining encoders better (multiple input frames before something comes
513// out). This should effectively turn off CPU adaptations for systems that
514// remotely cope with the load right now.
Henrik Boström62057622020-03-10 18:08:05515CpuOveruseOptions ResourceAdaptationProcessor::GetCpuOveruseOptions() const {
Henrik Boströmad515a22020-01-27 12:38:05516 // This is already ensured by the only caller of this method:
517 // StartResourceAdaptation().
518 RTC_DCHECK(encoder_settings_.has_value());
519 CpuOveruseOptions options;
520 // Hardware accelerated encoders are assumed to be pipelined; give them
521 // additional overuse time.
522 if (encoder_settings_->encoder_info().is_hardware_accelerated) {
523 options.low_encode_usage_threshold_percent = 150;
524 options.high_encode_usage_threshold_percent = 200;
525 }
526 if (experiment_cpu_load_estimator_) {
527 options.filter_time_ms = 5 * rtc::kNumMillisecsPerSec;
528 }
529 return options;
530}
531
Henrik Boström62057622020-03-10 18:08:05532int ResourceAdaptationProcessor::LastInputFrameSizeOrDefault() const {
Henrik Boströmd4578ae2020-01-22 15:16:04533 // The dependency on this hardcoded resolution is inherited from old code,
534 // which used this resolution as a stand-in for not knowing the resolution
535 // yet.
536 // TODO(hbos): Can we simply DCHECK has_value() before usage instead? Having a
537 // DCHECK passed all the tests but adding it does change the requirements of
Henrik Boström7875c992020-02-06 09:35:00538 // this class (= not being allowed to call OnResourceUnderuse() or
539 // OnResourceOveruse() before OnFrame()) and deserves a standalone CL.
Henrik Boström62057622020-03-10 18:08:05540 return last_input_frame_size_.value_or(kDefaultInputPixelsWidth *
541 kDefaultInputPixelsHeight);
Henrik Boströmd4578ae2020-01-22 15:16:04542}
543
Henrik Boström62057622020-03-10 18:08:05544void ResourceAdaptationProcessor::MaybeUpdateVideoSourceRestrictions() {
Henrik Boström07b17df2020-01-15 10:42:12545 VideoSourceRestrictions new_restrictions = ApplyDegradationPreference(
Henrik Boströmefbec9a2020-03-06 09:41:25546 stream_adapter_->source_restrictions(), degradation_preference_);
Henrik Boström07b17df2020-01-15 10:42:12547 if (video_source_restrictions_ != new_restrictions) {
548 video_source_restrictions_ = std::move(new_restrictions);
Henrik Boström8234b922020-01-13 16:26:50549 adaptation_listener_->OnVideoSourceRestrictionsUpdated(
Henrik Boström07b17df2020-01-15 10:42:12550 video_source_restrictions_);
Henrik Boströmfae6f0e2020-01-20 10:16:50551 MaybeUpdateTargetFrameRate();
552 }
553}
554
Henrik Boström62057622020-03-10 18:08:05555void ResourceAdaptationProcessor::MaybeUpdateTargetFrameRate() {
Henrik Boström4bab2fc2020-01-21 10:18:06556 absl::optional<double> codec_max_frame_rate =
557 encoder_settings_.has_value()
558 ? absl::optional<double>(
559 encoder_settings_->video_codec().maxFramerate)
560 : absl::nullopt;
Henrik Boströmfae6f0e2020-01-20 10:16:50561 // The current target framerate is the maximum frame rate as specified by
562 // the current codec configuration or any limit imposed by the adaptation
563 // module. This is used to make sure overuse detection doesn't needlessly
564 // trigger in low and/or variable framerate scenarios.
565 absl::optional<double> target_frame_rate =
Henrik Boströmefbec9a2020-03-06 09:41:25566 ApplyDegradationPreference(stream_adapter_->source_restrictions(),
Henrik Boströmfae6f0e2020-01-20 10:16:50567 degradation_preference_)
568 .max_frame_rate();
569 if (!target_frame_rate.has_value() ||
Henrik Boström4bab2fc2020-01-21 10:18:06570 (codec_max_frame_rate.has_value() &&
571 codec_max_frame_rate.value() < target_frame_rate.value())) {
572 target_frame_rate = codec_max_frame_rate;
Henrik Boströmfae6f0e2020-01-20 10:16:50573 }
Henrik Boström7875c992020-02-06 09:35:00574 encode_usage_resource_->SetTargetFrameRate(target_frame_rate);
Henrik Boström8234b922020-01-13 16:26:50575}
576
Henrik Boström62057622020-03-10 18:08:05577void ResourceAdaptationProcessor::OnAdaptationCountChanged(
Evan Shrubsole33be9df2020-03-05 17:39:32578 const AdaptationCounters& adaptation_count,
579 AdaptationCounters* active_count,
580 AdaptationCounters* other_active) {
581 RTC_DCHECK(active_count);
582 RTC_DCHECK(other_active);
583 const int active_total = active_count->Total();
584 const int other_total = other_active->Total();
585 const AdaptationCounters prev_total = *active_count + *other_active;
586 const AdaptationCounters delta = adaptation_count - prev_total;
587
588 RTC_DCHECK_EQ(
589 std::abs(delta.resolution_adaptations) + std::abs(delta.fps_adaptations),
590 1)
591 << "Adaptation took more than one step!";
592
593 if (delta.resolution_adaptations > 0) {
594 ++active_count->resolution_adaptations;
595 } else if (delta.resolution_adaptations < 0) {
596 if (active_count->resolution_adaptations == 0) {
597 RTC_DCHECK_GT(active_count->fps_adaptations, 0) << "No downgrades left";
598 RTC_DCHECK_GT(other_active->resolution_adaptations, 0)
599 << "No resolution adaptation to borrow from";
600 // Lend an fps adaptation to other and take one resolution adaptation.
601 --active_count->fps_adaptations;
602 ++other_active->fps_adaptations;
603 --other_active->resolution_adaptations;
604 } else {
605 --active_count->resolution_adaptations;
606 }
607 }
608 if (delta.fps_adaptations > 0) {
609 ++active_count->fps_adaptations;
610 } else if (delta.fps_adaptations < 0) {
611 if (active_count->fps_adaptations == 0) {
612 RTC_DCHECK_GT(active_count->resolution_adaptations, 0)
613 << "No downgrades left";
614 RTC_DCHECK_GT(other_active->fps_adaptations, 0)
615 << "No fps adaptation to borrow from";
616 // Lend a resolution adaptation to other and take one fps adaptation.
617 --active_count->resolution_adaptations;
618 ++other_active->resolution_adaptations;
619 --other_active->fps_adaptations;
620 } else {
621 --active_count->fps_adaptations;
622 }
623 }
624
625 RTC_DCHECK(*active_count + *other_active == adaptation_count);
626 RTC_DCHECK_EQ(other_active->Total(), other_total);
627 RTC_DCHECK_EQ(active_count->Total(), active_total + delta.Total());
628 RTC_DCHECK_GE(active_count->resolution_adaptations, 0);
629 RTC_DCHECK_GE(active_count->fps_adaptations, 0);
630 RTC_DCHECK_GE(other_active->resolution_adaptations, 0);
631 RTC_DCHECK_GE(other_active->fps_adaptations, 0);
632}
633
Henrik Boströmb08882b2020-01-07 09:11:17634// TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged.
Henrik Boström62057622020-03-10 18:08:05635void ResourceAdaptationProcessor::UpdateAdaptationStats(
Henrik Boström7875c992020-02-06 09:35:00636 AdaptationObserverInterface::AdaptReason reason) {
Evan Shrubsole33be9df2020-03-05 17:39:32637 // Update active counts
638 AdaptationCounters& active_count = active_counts_[reason];
639 AdaptationCounters& other_active = active_counts_[(reason + 1) % 2];
640 const AdaptationCounters total_counts =
Henrik Boströmefbec9a2020-03-06 09:41:25641 stream_adapter_->adaptation_counters();
Evan Shrubsole33be9df2020-03-05 17:39:32642
643 OnAdaptationCountChanged(total_counts, &active_count, &other_active);
644
Henrik Boströmb08882b2020-01-07 09:11:17645 switch (reason) {
Henrik Boström7875c992020-02-06 09:35:00646 case AdaptationObserverInterface::AdaptReason::kCpu:
Henrik Boströmb08882b2020-01-07 09:11:17647 encoder_stats_observer_->OnAdaptationChanged(
648 VideoStreamEncoderObserver::AdaptationReason::kCpu,
Henrik Boström7875c992020-02-06 09:35:00649 GetActiveCounts(AdaptationObserverInterface::AdaptReason::kCpu),
650 GetActiveCounts(AdaptationObserverInterface::AdaptReason::kQuality));
Henrik Boströmb08882b2020-01-07 09:11:17651 break;
Henrik Boström7875c992020-02-06 09:35:00652 case AdaptationObserverInterface::AdaptReason::kQuality:
Henrik Boströmb08882b2020-01-07 09:11:17653 encoder_stats_observer_->OnAdaptationChanged(
654 VideoStreamEncoderObserver::AdaptationReason::kQuality,
Henrik Boström7875c992020-02-06 09:35:00655 GetActiveCounts(AdaptationObserverInterface::AdaptReason::kCpu),
656 GetActiveCounts(AdaptationObserverInterface::AdaptReason::kQuality));
Henrik Boströmb08882b2020-01-07 09:11:17657 break;
658 }
659}
660
661VideoStreamEncoderObserver::AdaptationSteps
Henrik Boström62057622020-03-10 18:08:05662ResourceAdaptationProcessor::GetActiveCounts(
Henrik Boström7875c992020-02-06 09:35:00663 AdaptationObserverInterface::AdaptReason reason) {
Evan Shrubsole33be9df2020-03-05 17:39:32664 // TODO(https://crbug.com/webrtc/11392) Ideally this shuold be moved out of
665 // this class and into the encoder_stats_observer_.
666 const AdaptationCounters counters = active_counts_[reason];
667
Henrik Boströmb08882b2020-01-07 09:11:17668 VideoStreamEncoderObserver::AdaptationSteps counts =
Evan Shrubsole33be9df2020-03-05 17:39:32669 VideoStreamEncoderObserver::AdaptationSteps();
670 counts.num_resolution_reductions = counters.resolution_adaptations;
671 counts.num_framerate_reductions = counters.fps_adaptations;
Henrik Boströmb08882b2020-01-07 09:11:17672 switch (reason) {
Henrik Boström7875c992020-02-06 09:35:00673 case AdaptationObserverInterface::AdaptReason::kCpu:
Henrik Boströmb08882b2020-01-07 09:11:17674 if (!IsFramerateScalingEnabled(degradation_preference_))
675 counts.num_framerate_reductions = absl::nullopt;
676 if (!IsResolutionScalingEnabled(degradation_preference_))
677 counts.num_resolution_reductions = absl::nullopt;
678 break;
Henrik Boström7875c992020-02-06 09:35:00679 case AdaptationObserverInterface::AdaptReason::kQuality:
Henrik Boströmb08882b2020-01-07 09:11:17680 if (!IsFramerateScalingEnabled(degradation_preference_) ||
Henrik Boström7875c992020-02-06 09:35:00681 !quality_scaler_resource_->is_started()) {
Henrik Boströmb08882b2020-01-07 09:11:17682 counts.num_framerate_reductions = absl::nullopt;
683 }
684 if (!IsResolutionScalingEnabled(degradation_preference_) ||
Henrik Boström7875c992020-02-06 09:35:00685 !quality_scaler_resource_->is_started()) {
Henrik Boströmb08882b2020-01-07 09:11:17686 counts.num_resolution_reductions = absl::nullopt;
687 }
688 break;
689 }
690 return counts;
691}
692
Henrik Boströmb0f2e0c2020-03-06 12:32:03693VideoStreamAdapter::VideoInputMode
Henrik Boström62057622020-03-10 18:08:05694ResourceAdaptationProcessor::GetVideoInputMode() const {
Henrik Boströmb0f2e0c2020-03-06 12:32:03695 if (!has_input_video_)
696 return VideoStreamAdapter::VideoInputMode::kNoVideo;
Henrik Boström4bab2fc2020-01-21 10:18:06697 return (encoder_settings_.has_value() &&
698 encoder_settings_->encoder_config().content_type ==
Henrik Boströmb0f2e0c2020-03-06 12:32:03699 VideoEncoderConfig::ContentType::kScreen)
700 ? VideoStreamAdapter::VideoInputMode::kScreenshareVideo
701 : VideoStreamAdapter::VideoInputMode::kNormalVideo;
Henrik Boströmb08882b2020-01-07 09:11:17702}
703
Henrik Boström62057622020-03-10 18:08:05704void ResourceAdaptationProcessor::MaybePerformQualityRampupExperiment() {
Henrik Boström7875c992020-02-06 09:35:00705 if (!quality_scaler_resource_->is_started())
Evan Shrubsolee331a122020-02-05 12:30:33706 return;
707
708 if (quality_rampup_done_)
709 return;
710
711 int64_t now_ms = clock_->TimeInMilliseconds();
712 uint32_t bw_kbps = encoder_rates_.has_value()
713 ? encoder_rates_.value().bandwidth_allocation.kbps()
714 : 0;
715
716 bool try_quality_rampup = false;
717 if (quality_rampup_experiment_.BwHigh(now_ms, bw_kbps)) {
718 // Verify that encoder is at max bitrate and the QP is low.
719 if (encoder_settings_ &&
720 encoder_target_bitrate_bps_.value_or(0) ==
721 encoder_settings_->video_codec().maxBitrate * 1000 &&
Henrik Boström7875c992020-02-06 09:35:00722 quality_scaler_resource_->QpFastFilterLow()) {
Evan Shrubsolee331a122020-02-05 12:30:33723 try_quality_rampup = true;
724 }
725 }
Evan Shrubsole33be9df2020-03-05 17:39:32726 // TODO(https://crbug.com/webrtc/11392): See if we can rely on the total
727 // counts or the stats, and not the active counts.
728 const AdaptationCounters& qp_counts =
729 std::get<AdaptationObserverInterface::kQuality>(active_counts_);
730 const AdaptationCounters& cpu_counts =
731 std::get<AdaptationObserverInterface::kCpu>(active_counts_);
732 if (try_quality_rampup && qp_counts.resolution_adaptations > 0 &&
733 cpu_counts.Total() == 0) {
Evan Shrubsolee331a122020-02-05 12:30:33734 RTC_LOG(LS_INFO) << "Reset quality limitations.";
735 ResetVideoSourceRestrictions();
736 quality_rampup_done_ = true;
737 }
738}
739
Henrik Boström62057622020-03-10 18:08:05740std::string ResourceAdaptationProcessor::ActiveCountsToString() const {
Evan Shrubsole33be9df2020-03-05 17:39:32741 rtc::StringBuilder ss;
742
743 ss << "Downgrade counts: fps: {";
744 for (size_t reason = 0; reason < active_counts_.size(); ++reason) {
745 ss << (reason ? " cpu" : "quality") << ":";
746 ss << active_counts_[reason].fps_adaptations;
747 }
748 ss << "}, resolution {";
749 for (size_t reason = 0; reason < active_counts_.size(); ++reason) {
750 ss << (reason ? " cpu" : "quality") << ":";
751 ss << active_counts_[reason].resolution_adaptations;
752 }
753 ss << "}";
754
755 return ss.Release();
756}
Henrik Boströmb08882b2020-01-07 09:11:17757} // namespace webrtc