blob: 7a35b64e22c691f82afdfb8ff3d0e69153f6291e [file] [log] [blame]
Henrik Boströmefbec9a2020-03-06 10:41:25 +01001/*
2 * Copyright 2020 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/adaptation/video_stream_adapter.h"
12
13#include <algorithm>
14#include <limits>
Henrik Boström34531492020-03-12 12:41:19 +010015#include <utility>
Henrik Boströmefbec9a2020-03-06 10:41:25 +010016
17#include "absl/types/optional.h"
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +010018#include "api/video_codecs/video_encoder.h"
Henrik Boströmefbec9a2020-03-06 10:41:25 +010019#include "rtc_base/constructor_magic.h"
20#include "rtc_base/logging.h"
21#include "rtc_base/numerics/safe_conversions.h"
22
23namespace webrtc {
24
Henrik Boström4c076052020-03-18 10:09:59 +010025const int kMinFrameRateFps = 2;
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +010026
Henrik Boström4c076052020-03-18 10:09:59 +010027namespace {
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +010028
Henrik Boström8d9f7502020-03-06 14:51:34 +010029int MinPixelsPerFrame(const absl::optional<EncoderSettings>& encoder_settings) {
30 return encoder_settings.has_value()
31 ? encoder_settings->encoder_info()
32 .scaling_settings.min_pixels_per_frame
33 : kDefaultMinPixelsPerFrame;
34}
35
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +010036// Generate suggested higher and lower frame rates and resolutions, to be
37// applied to the VideoSourceRestrictor. These are used in "maintain-resolution"
38// and "maintain-framerate". The "balanced" degradation preference also makes
39// use of BalancedDegradationPreference when generating suggestions. The
40// VideoSourceRestrictor decidedes whether or not a proposed adaptation is
41// valid.
42
43// For frame rate, the steps we take are 2/3 (down) and 3/2 (up).
44int GetLowerFrameRateThan(int fps) {
45 RTC_DCHECK(fps != std::numeric_limits<int>::max());
46 return (fps * 2) / 3;
47}
48// TODO(hbos): Use absl::optional<> instead?
49int GetHigherFrameRateThan(int fps) {
50 return fps != std::numeric_limits<int>::max()
51 ? (fps * 3) / 2
52 : std::numeric_limits<int>::max();
53}
54
55// For resolution, the steps we take are 3/5 (down) and 5/3 (up).
56// Notice the asymmetry of which restriction property is set depending on if
57// we are adapting up or down:
58// - VideoSourceRestrictor::DecreaseResolution() sets the max_pixels_per_frame()
59// to the desired target and target_pixels_per_frame() to null.
60// - VideoSourceRestrictor::IncreaseResolutionTo() sets the
61// target_pixels_per_frame() to the desired target, and max_pixels_per_frame()
62// is set according to VideoSourceRestrictor::GetIncreasedMaxPixelsWanted().
63int GetLowerResolutionThan(int pixel_count) {
64 RTC_DCHECK(pixel_count != std::numeric_limits<int>::max());
65 return (pixel_count * 3) / 5;
66}
67// TODO(hbos): Use absl::optional<> instead?
68int GetHigherResolutionThan(int pixel_count) {
69 return pixel_count != std::numeric_limits<int>::max()
70 ? (pixel_count * 5) / 3
71 : std::numeric_limits<int>::max();
72}
73
Henrik Boström453953c2020-03-14 10:53:57 +010074// One of the conditions used in VideoStreamAdapter::GetAdaptationUp().
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +010075// TODO(hbos): Whether or not we can adapt up due to encoder settings and
76// bitrate should be expressed as a bandwidth-related Resource.
77bool CanAdaptUpResolution(
78 const absl::optional<EncoderSettings>& encoder_settings,
79 absl::optional<uint32_t> encoder_target_bitrate_bps,
80 int input_pixels) {
81 uint32_t bitrate_bps = encoder_target_bitrate_bps.value_or(0);
82 absl::optional<VideoEncoder::ResolutionBitrateLimits> bitrate_limits =
83 encoder_settings.has_value()
84 ? encoder_settings->encoder_info()
85 .GetEncoderBitrateLimitsForResolution(
86 GetHigherResolutionThan(input_pixels))
87 : absl::nullopt;
88 if (!bitrate_limits.has_value() || bitrate_bps == 0) {
89 return true; // No limit configured or bitrate provided.
90 }
91 RTC_DCHECK_GE(bitrate_limits->frame_size_pixels, input_pixels);
92 return bitrate_bps >=
93 static_cast<uint32_t>(bitrate_limits->min_start_bitrate_bps);
94}
95
96} // namespace
97
Henrik Boström453953c2020-03-14 10:53:57 +010098Adaptation::Step::Step(StepType type, int target)
99 : type(type), target(target) {}
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100100
Henrik Boström453953c2020-03-14 10:53:57 +0100101Adaptation::Adaptation(int validation_id, Step step)
102 : validation_id_(validation_id),
103 status_(Status::kValid),
104 step_(std::move(step)),
105 min_pixel_limit_reached_(false) {}
106
107Adaptation::Adaptation(int validation_id,
108 Step step,
109 bool min_pixel_limit_reached)
110 : validation_id_(validation_id),
111 status_(Status::kValid),
112 step_(std::move(step)),
Henrik Boström34531492020-03-12 12:41:19 +0100113 min_pixel_limit_reached_(min_pixel_limit_reached) {}
114
Henrik Boström453953c2020-03-14 10:53:57 +0100115Adaptation::Adaptation(int validation_id, Status invalid_status)
116 : validation_id_(validation_id),
117 status_(invalid_status),
118 step_(absl::nullopt),
119 min_pixel_limit_reached_(false) {
120 RTC_DCHECK_NE(status_, Status::kValid);
Henrik Boström34531492020-03-12 12:41:19 +0100121}
122
Henrik Boström453953c2020-03-14 10:53:57 +0100123Adaptation::Adaptation(int validation_id,
124 Status invalid_status,
125 bool min_pixel_limit_reached)
126 : validation_id_(validation_id),
127 status_(invalid_status),
128 step_(absl::nullopt),
129 min_pixel_limit_reached_(min_pixel_limit_reached) {
130 RTC_DCHECK_NE(status_, Status::kValid);
Henrik Boström34531492020-03-12 12:41:19 +0100131}
132
Henrik Boström453953c2020-03-14 10:53:57 +0100133Adaptation::Status Adaptation::status() const {
134 return status_;
Henrik Boström34531492020-03-12 12:41:19 +0100135}
136
Henrik Boström453953c2020-03-14 10:53:57 +0100137bool Adaptation::min_pixel_limit_reached() const {
Henrik Boström34531492020-03-12 12:41:19 +0100138 return min_pixel_limit_reached_;
139}
140
Henrik Boström453953c2020-03-14 10:53:57 +0100141const Adaptation::Step& Adaptation::step() const {
142 RTC_DCHECK_EQ(status_, Status::kValid);
143 return step_.value();
144}
145
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100146// VideoSourceRestrictor is responsible for keeping track of current
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100147// VideoSourceRestrictions.
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100148class VideoStreamAdapter::VideoSourceRestrictor {
149 public:
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100150 VideoSourceRestrictor() {}
151
152 VideoSourceRestrictions source_restrictions() const {
153 return source_restrictions_;
154 }
155 const AdaptationCounters& adaptation_counters() const { return adaptations_; }
156 void ClearRestrictions() {
157 source_restrictions_ = VideoSourceRestrictions();
158 adaptations_ = AdaptationCounters();
159 }
160
Henrik Boström453953c2020-03-14 10:53:57 +0100161 void SetMinPixelsPerFrame(int min_pixels_per_frame) {
162 min_pixels_per_frame_ = min_pixels_per_frame;
163 }
164
165 bool CanDecreaseResolutionTo(int target_pixels) {
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100166 int max_pixels_per_frame = rtc::dchecked_cast<int>(
167 source_restrictions_.max_pixels_per_frame().value_or(
168 std::numeric_limits<int>::max()));
169 return target_pixels < max_pixels_per_frame &&
Henrik Boström453953c2020-03-14 10:53:57 +0100170 target_pixels >= min_pixels_per_frame_;
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100171 }
172
173 bool CanIncreaseResolutionTo(int target_pixels) {
174 int max_pixels_wanted = GetIncreasedMaxPixelsWanted(target_pixels);
175 int max_pixels_per_frame = rtc::dchecked_cast<int>(
176 source_restrictions_.max_pixels_per_frame().value_or(
177 std::numeric_limits<int>::max()));
178 return max_pixels_wanted > max_pixels_per_frame;
179 }
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100180
181 bool CanDecreaseFrameRateTo(int max_frame_rate) {
Henrik Boström4c076052020-03-18 10:09:59 +0100182 const int fps_wanted = std::max(kMinFrameRateFps, max_frame_rate);
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100183 return fps_wanted < rtc::dchecked_cast<int>(
184 source_restrictions_.max_frame_rate().value_or(
185 std::numeric_limits<int>::max()));
186 }
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100187
188 bool CanIncreaseFrameRateTo(int max_frame_rate) {
189 return max_frame_rate > rtc::dchecked_cast<int>(
190 source_restrictions_.max_frame_rate().value_or(
191 std::numeric_limits<int>::max()));
192 }
Henrik Boström453953c2020-03-14 10:53:57 +0100193
194 void ApplyAdaptationStep(
195 const Adaptation::Step& step,
196 DegradationPreference effective_degradation_preference) {
197 switch (step.type) {
198 case Adaptation::StepType::kIncreaseResolution:
199 IncreaseResolutionTo(step.target);
200 break;
201 case Adaptation::StepType::kDecreaseResolution:
202 DecreaseResolutionTo(step.target);
203 break;
204 case Adaptation::StepType::kIncreaseFrameRate:
205 IncreaseFrameRateTo(step.target);
206 // TODO(https://crbug.com/webrtc/11222): Don't adapt in two steps.
207 // GetAdaptationUp() should tell us the correct value, but BALANCED
208 // logic in DecrementFramerate() makes it hard to predict whether this
209 // will be the last step. Remove the dependency on
210 // adaptation_counters().
211 if (effective_degradation_preference ==
212 DegradationPreference::BALANCED &&
213 adaptation_counters().fps_adaptations == 0 &&
214 step.target != std::numeric_limits<int>::max()) {
215 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
216 IncreaseFrameRateTo(std::numeric_limits<int>::max());
217 }
218 break;
219 case Adaptation::StepType::kDecreaseFrameRate:
220 DecreaseFrameRateTo(step.target);
221 break;
222 }
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100223 }
224
225 private:
226 static int GetIncreasedMaxPixelsWanted(int target_pixels) {
227 if (target_pixels == std::numeric_limits<int>::max())
228 return std::numeric_limits<int>::max();
229 // When we decrease resolution, we go down to at most 3/5 of current pixels.
230 // Thus to increase resolution, we need 3/5 to get back to where we started.
231 // When going up, the desired max_pixels_per_frame() has to be significantly
232 // higher than the target because the source's native resolutions might not
233 // match the target. We pick 12/5 of the target.
234 //
235 // (This value was historically 4 times the old target, which is (3/5)*4 of
236 // the new target - or 12/5 - assuming the target is adjusted according to
237 // the above steps.)
238 RTC_DCHECK(target_pixels != std::numeric_limits<int>::max());
239 return (target_pixels * 12) / 5;
240 }
241
Henrik Boström453953c2020-03-14 10:53:57 +0100242 void DecreaseResolutionTo(int target_pixels) {
243 RTC_DCHECK(CanDecreaseResolutionTo(target_pixels));
244 RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: "
245 << target_pixels;
246 source_restrictions_.set_max_pixels_per_frame(
247 target_pixels != std::numeric_limits<int>::max()
248 ? absl::optional<size_t>(target_pixels)
249 : absl::nullopt);
250 source_restrictions_.set_target_pixels_per_frame(absl::nullopt);
251 ++adaptations_.resolution_adaptations;
252 }
253
254 void IncreaseResolutionTo(int target_pixels) {
255 RTC_DCHECK(CanIncreaseResolutionTo(target_pixels));
256 int max_pixels_wanted = GetIncreasedMaxPixelsWanted(target_pixels);
257 RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
258 << max_pixels_wanted;
259 source_restrictions_.set_max_pixels_per_frame(
260 max_pixels_wanted != std::numeric_limits<int>::max()
261 ? absl::optional<size_t>(max_pixels_wanted)
262 : absl::nullopt);
263 source_restrictions_.set_target_pixels_per_frame(
264 max_pixels_wanted != std::numeric_limits<int>::max()
265 ? absl::optional<size_t>(target_pixels)
266 : absl::nullopt);
267 --adaptations_.resolution_adaptations;
268 RTC_DCHECK_GE(adaptations_.resolution_adaptations, 0);
269 }
270
271 void DecreaseFrameRateTo(int max_frame_rate) {
272 RTC_DCHECK(CanDecreaseFrameRateTo(max_frame_rate));
Henrik Boström4c076052020-03-18 10:09:59 +0100273 max_frame_rate = std::max(kMinFrameRateFps, max_frame_rate);
Henrik Boström453953c2020-03-14 10:53:57 +0100274 RTC_LOG(LS_INFO) << "Scaling down framerate: " << max_frame_rate;
275 source_restrictions_.set_max_frame_rate(
276 max_frame_rate != std::numeric_limits<int>::max()
277 ? absl::optional<double>(max_frame_rate)
278 : absl::nullopt);
279 ++adaptations_.fps_adaptations;
280 }
281
282 void IncreaseFrameRateTo(int max_frame_rate) {
283 RTC_DCHECK(CanIncreaseFrameRateTo(max_frame_rate));
284 RTC_LOG(LS_INFO) << "Scaling up framerate: " << max_frame_rate;
285 source_restrictions_.set_max_frame_rate(
286 max_frame_rate != std::numeric_limits<int>::max()
287 ? absl::optional<double>(max_frame_rate)
288 : absl::nullopt);
289 --adaptations_.fps_adaptations;
290 RTC_DCHECK_GE(adaptations_.fps_adaptations, 0);
291 }
292
293 // Needed by CanDecreaseResolutionTo().
294 int min_pixels_per_frame_ = 0;
295 // Current State.
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100296 VideoSourceRestrictions source_restrictions_;
297 AdaptationCounters adaptations_;
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100298};
299
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100300// static
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100301VideoStreamAdapter::AdaptationRequest::Mode
302VideoStreamAdapter::AdaptationRequest::GetModeFromAdaptationAction(
Henrik Boström453953c2020-03-14 10:53:57 +0100303 Adaptation::StepType step_type) {
304 switch (step_type) {
305 case Adaptation::StepType::kIncreaseResolution:
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100306 return AdaptationRequest::Mode::kAdaptUp;
Henrik Boström453953c2020-03-14 10:53:57 +0100307 case Adaptation::StepType::kDecreaseResolution:
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100308 return AdaptationRequest::Mode::kAdaptDown;
Henrik Boström453953c2020-03-14 10:53:57 +0100309 case Adaptation::StepType::kIncreaseFrameRate:
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100310 return AdaptationRequest::Mode::kAdaptUp;
Henrik Boström453953c2020-03-14 10:53:57 +0100311 case Adaptation::StepType::kDecreaseFrameRate:
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100312 return AdaptationRequest::Mode::kAdaptDown;
313 }
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100314}
315
316VideoStreamAdapter::VideoStreamAdapter()
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100317 : source_restrictor_(std::make_unique<VideoSourceRestrictor>()),
318 balanced_settings_(),
Henrik Boström453953c2020-03-14 10:53:57 +0100319 adaptation_validation_id_(0),
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100320 degradation_preference_(DegradationPreference::DISABLED),
Henrik Boström453953c2020-03-14 10:53:57 +0100321 input_mode_(VideoInputMode::kNoVideo),
322 input_pixels_(0),
323 input_fps_(0),
324 encoder_settings_(absl::nullopt),
325 encoder_target_bitrate_bps_(absl::nullopt),
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100326 last_adaptation_request_(absl::nullopt) {}
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100327
328VideoStreamAdapter::~VideoStreamAdapter() {}
329
330VideoSourceRestrictions VideoStreamAdapter::source_restrictions() const {
331 return source_restrictor_->source_restrictions();
332}
333
334const AdaptationCounters& VideoStreamAdapter::adaptation_counters() const {
335 return source_restrictor_->adaptation_counters();
336}
337
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100338const BalancedDegradationSettings& VideoStreamAdapter::balanced_settings()
339 const {
340 return balanced_settings_;
341}
342
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100343void VideoStreamAdapter::ClearRestrictions() {
Henrik Boström453953c2020-03-14 10:53:57 +0100344 // Invalidate any previously returned Adaptation.
345 ++adaptation_validation_id_;
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100346 source_restrictor_->ClearRestrictions();
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100347 last_adaptation_request_.reset();
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100348}
349
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100350VideoStreamAdapter::SetDegradationPreferenceResult
351VideoStreamAdapter::SetDegradationPreference(
352 DegradationPreference degradation_preference) {
Henrik Boström453953c2020-03-14 10:53:57 +0100353 if (degradation_preference_ == degradation_preference)
354 return SetDegradationPreferenceResult::kRestrictionsNotCleared;
355 // Invalidate any previously returned Adaptation.
356 ++adaptation_validation_id_;
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100357 bool did_clear = false;
Henrik Boström453953c2020-03-14 10:53:57 +0100358 if (degradation_preference == DegradationPreference::BALANCED ||
359 degradation_preference_ == DegradationPreference::BALANCED) {
360 ClearRestrictions();
361 did_clear = true;
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100362 }
363 degradation_preference_ = degradation_preference;
364 return did_clear ? SetDegradationPreferenceResult::kRestrictionsCleared
365 : SetDegradationPreferenceResult::kRestrictionsNotCleared;
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100366}
367
Henrik Boström453953c2020-03-14 10:53:57 +0100368void VideoStreamAdapter::SetInput(
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100369 VideoInputMode input_mode,
370 int input_pixels,
371 int input_fps,
Henrik Boström453953c2020-03-14 10:53:57 +0100372 absl::optional<EncoderSettings> encoder_settings,
373 absl::optional<uint32_t> encoder_target_bitrate_bps) {
374 // Invalidate any previously returned Adaptation.
375 ++adaptation_validation_id_;
376 input_mode_ = input_mode;
377 input_pixels_ = input_pixels;
378 input_fps_ = input_fps;
379 encoder_settings_ = encoder_settings;
380 encoder_target_bitrate_bps_ = encoder_target_bitrate_bps;
381 source_restrictor_->SetMinPixelsPerFrame(
382 MinPixelsPerFrame(encoder_settings_));
383}
384
385Adaptation VideoStreamAdapter::GetAdaptationUp(
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100386 AdaptationObserverInterface::AdaptReason reason) const {
Henrik Boström34531492020-03-12 12:41:19 +0100387 // Don't adapt if we don't have sufficient input.
Henrik Boström453953c2020-03-14 10:53:57 +0100388 if (input_mode_ == VideoInputMode::kNoVideo) {
389 return Adaptation(adaptation_validation_id_,
390 Adaptation::Status::kInsufficientInput);
Henrik Boström34531492020-03-12 12:41:19 +0100391 }
392 // Don't adapt if we're awaiting a previous adaptation to have an effect.
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100393 bool last_adaptation_was_up =
394 last_adaptation_request_ &&
395 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp;
396 if (last_adaptation_was_up &&
397 degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
Henrik Boström453953c2020-03-14 10:53:57 +0100398 input_pixels_ <= last_adaptation_request_->input_pixel_count_) {
399 return Adaptation(adaptation_validation_id_,
400 Adaptation::Status::kAwaitingPreviousAdaptation);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100401 }
Henrik Boström34531492020-03-12 12:41:19 +0100402 // Don't adapt if BalancedDegradationSettings applies and determines this will
403 // exceed bitrate constraints.
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100404 if (reason == AdaptationObserverInterface::AdaptReason::kQuality &&
Henrik Boström453953c2020-03-14 10:53:57 +0100405 EffectiveDegradationPreference() == DegradationPreference::BALANCED &&
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100406 !balanced_settings_.CanAdaptUp(
Henrik Boström453953c2020-03-14 10:53:57 +0100407 GetVideoCodecTypeOrGeneric(encoder_settings_), input_pixels_,
408 encoder_target_bitrate_bps_.value_or(0))) {
409 return Adaptation(adaptation_validation_id_,
410 Adaptation::Status::kIsBitrateConstrained);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100411 }
412
Henrik Boström34531492020-03-12 12:41:19 +0100413 // Maybe propose targets based on degradation preference.
Henrik Boström453953c2020-03-14 10:53:57 +0100414 switch (EffectiveDegradationPreference()) {
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100415 case DegradationPreference::BALANCED: {
416 // Attempt to increase target frame rate.
417 int target_fps = balanced_settings_.MaxFps(
Henrik Boström453953c2020-03-14 10:53:57 +0100418 GetVideoCodecTypeOrGeneric(encoder_settings_), input_pixels_);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100419 if (source_restrictor_->CanIncreaseFrameRateTo(target_fps)) {
Henrik Boström453953c2020-03-14 10:53:57 +0100420 return Adaptation(
421 adaptation_validation_id_,
422 Adaptation::Step(Adaptation::StepType::kIncreaseFrameRate,
423 target_fps));
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100424 }
425 // Fall-through to maybe-adapting resolution, unless |balanced_settings_|
426 // forbids it based on bitrate.
427 if (reason == AdaptationObserverInterface::AdaptReason::kQuality &&
428 !balanced_settings_.CanAdaptUpResolution(
Henrik Boström453953c2020-03-14 10:53:57 +0100429 GetVideoCodecTypeOrGeneric(encoder_settings_), input_pixels_,
430 encoder_target_bitrate_bps_.value_or(0))) {
431 return Adaptation(adaptation_validation_id_,
432 Adaptation::Status::kIsBitrateConstrained);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100433 }
434 // Scale up resolution.
435 ABSL_FALLTHROUGH_INTENDED;
436 }
437 case DegradationPreference::MAINTAIN_FRAMERATE: {
438 // Don't adapt resolution if CanAdaptUpResolution() forbids it based on
439 // bitrate and limits specified by encoder capabilities.
440 if (reason == AdaptationObserverInterface::AdaptReason::kQuality &&
Henrik Boström453953c2020-03-14 10:53:57 +0100441 !CanAdaptUpResolution(encoder_settings_, encoder_target_bitrate_bps_,
442 input_pixels_)) {
443 return Adaptation(adaptation_validation_id_,
444 Adaptation::Status::kIsBitrateConstrained);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100445 }
446 // Attempt to increase pixel count.
Henrik Boström453953c2020-03-14 10:53:57 +0100447 int target_pixels = input_pixels_;
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100448 if (source_restrictor_->adaptation_counters().resolution_adaptations ==
449 1) {
450 RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
451 target_pixels = std::numeric_limits<int>::max();
452 }
453 target_pixels = GetHigherResolutionThan(target_pixels);
Henrik Boström34531492020-03-12 12:41:19 +0100454 if (!source_restrictor_->CanIncreaseResolutionTo(target_pixels)) {
Henrik Boström453953c2020-03-14 10:53:57 +0100455 return Adaptation(adaptation_validation_id_,
456 Adaptation::Status::kLimitReached);
Henrik Boström34531492020-03-12 12:41:19 +0100457 }
Henrik Boström453953c2020-03-14 10:53:57 +0100458 return Adaptation(
459 adaptation_validation_id_,
460 Adaptation::Step(Adaptation::StepType::kIncreaseResolution,
461 target_pixels));
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100462 }
463 case DegradationPreference::MAINTAIN_RESOLUTION: {
464 // Scale up framerate.
Henrik Boström453953c2020-03-14 10:53:57 +0100465 int target_fps = input_fps_;
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100466 if (source_restrictor_->adaptation_counters().fps_adaptations == 1) {
467 RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
468 target_fps = std::numeric_limits<int>::max();
469 }
470 target_fps = GetHigherFrameRateThan(target_fps);
Henrik Boström34531492020-03-12 12:41:19 +0100471 if (!source_restrictor_->CanIncreaseFrameRateTo(target_fps)) {
Henrik Boström453953c2020-03-14 10:53:57 +0100472 return Adaptation(adaptation_validation_id_,
473 Adaptation::Status::kLimitReached);
Henrik Boström34531492020-03-12 12:41:19 +0100474 }
Henrik Boström453953c2020-03-14 10:53:57 +0100475 return Adaptation(
476 adaptation_validation_id_,
477 Adaptation::Step(Adaptation::StepType::kIncreaseFrameRate,
478 target_fps));
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100479 }
480 case DegradationPreference::DISABLED:
Henrik Boström453953c2020-03-14 10:53:57 +0100481 return Adaptation(adaptation_validation_id_,
482 Adaptation::Status::kAdaptationDisabled);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100483 }
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100484}
485
Henrik Boström453953c2020-03-14 10:53:57 +0100486Adaptation VideoStreamAdapter::GetAdaptationDown() const {
Henrik Boström34531492020-03-12 12:41:19 +0100487 // Don't adapt if we don't have sufficient input or adaptation is disabled.
Henrik Boström453953c2020-03-14 10:53:57 +0100488 if (input_mode_ == VideoInputMode::kNoVideo) {
489 return Adaptation(adaptation_validation_id_,
490 Adaptation::Status::kInsufficientInput);
Henrik Boström34531492020-03-12 12:41:19 +0100491 }
492 if (degradation_preference_ == DegradationPreference::DISABLED) {
Henrik Boström453953c2020-03-14 10:53:57 +0100493 return Adaptation(adaptation_validation_id_,
494 Adaptation::Status::kAdaptationDisabled);
Henrik Boström34531492020-03-12 12:41:19 +0100495 }
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100496 bool last_adaptation_was_down =
497 last_adaptation_request_ &&
498 last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown;
Henrik Boström453953c2020-03-14 10:53:57 +0100499 if (EffectiveDegradationPreference() ==
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100500 DegradationPreference::MAINTAIN_RESOLUTION) {
501 // TODO(hbos): This usage of |last_adaptation_was_down| looks like a mistake
502 // - delete it.
Henrik Boström453953c2020-03-14 10:53:57 +0100503 if (input_fps_ <= 0 ||
Henrik Boström4c076052020-03-18 10:09:59 +0100504 (last_adaptation_was_down && input_fps_ < kMinFrameRateFps)) {
Henrik Boström453953c2020-03-14 10:53:57 +0100505 return Adaptation(adaptation_validation_id_,
506 Adaptation::Status::kInsufficientInput);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100507 }
508 }
Henrik Boström34531492020-03-12 12:41:19 +0100509 // Don't adapt if we're awaiting a previous adaptation to have an effect.
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100510 if (last_adaptation_was_down &&
511 degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
Henrik Boström453953c2020-03-14 10:53:57 +0100512 input_pixels_ >= last_adaptation_request_->input_pixel_count_) {
513 return Adaptation(adaptation_validation_id_,
514 Adaptation::Status::kAwaitingPreviousAdaptation);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100515 }
516
Henrik Boström34531492020-03-12 12:41:19 +0100517 // Maybe propose targets based on degradation preference.
Henrik Boström453953c2020-03-14 10:53:57 +0100518 switch (EffectiveDegradationPreference()) {
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100519 case DegradationPreference::BALANCED: {
520 // Try scale down framerate, if lower.
521 int target_fps = balanced_settings_.MinFps(
Henrik Boström453953c2020-03-14 10:53:57 +0100522 GetVideoCodecTypeOrGeneric(encoder_settings_), input_pixels_);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100523 if (source_restrictor_->CanDecreaseFrameRateTo(target_fps)) {
Henrik Boström453953c2020-03-14 10:53:57 +0100524 return Adaptation(
525 adaptation_validation_id_,
526 Adaptation::Step(Adaptation::StepType::kDecreaseFrameRate,
527 target_fps));
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100528 }
529 // Scale down resolution.
530 ABSL_FALLTHROUGH_INTENDED;
531 }
532 case DegradationPreference::MAINTAIN_FRAMERATE: {
533 // Scale down resolution.
Henrik Boström453953c2020-03-14 10:53:57 +0100534 int target_pixels = GetLowerResolutionThan(input_pixels_);
535 bool min_pixel_limit_reached =
536 target_pixels < MinPixelsPerFrame(encoder_settings_);
537 if (!source_restrictor_->CanDecreaseResolutionTo(target_pixels)) {
538 return Adaptation(adaptation_validation_id_,
539 Adaptation::Status::kLimitReached,
540 min_pixel_limit_reached);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100541 }
Henrik Boström453953c2020-03-14 10:53:57 +0100542 return Adaptation(
543 adaptation_validation_id_,
544 Adaptation::Step(Adaptation::StepType::kDecreaseResolution,
Henrik Boström34531492020-03-12 12:41:19 +0100545 target_pixels),
546 min_pixel_limit_reached);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100547 }
548 case DegradationPreference::MAINTAIN_RESOLUTION: {
Henrik Boström453953c2020-03-14 10:53:57 +0100549 int target_fps = GetLowerFrameRateThan(input_fps_);
Henrik Boström34531492020-03-12 12:41:19 +0100550 if (!source_restrictor_->CanDecreaseFrameRateTo(target_fps)) {
Henrik Boström453953c2020-03-14 10:53:57 +0100551 return Adaptation(adaptation_validation_id_,
552 Adaptation::Status::kLimitReached);
Henrik Boström34531492020-03-12 12:41:19 +0100553 }
Henrik Boström453953c2020-03-14 10:53:57 +0100554 return Adaptation(
555 adaptation_validation_id_,
556 Adaptation::Step(Adaptation::StepType::kDecreaseFrameRate,
557 target_fps));
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100558 }
559 case DegradationPreference::DISABLED:
560 RTC_NOTREACHED();
Henrik Boström453953c2020-03-14 10:53:57 +0100561 return Adaptation(adaptation_validation_id_,
562 Adaptation::Status::kAdaptationDisabled);
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100563 }
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100564}
565
Henrik Boström453953c2020-03-14 10:53:57 +0100566VideoSourceRestrictions VideoStreamAdapter::PeekNextRestrictions(
567 const Adaptation& adaptation) const {
Henrik Boström4c076052020-03-18 10:09:59 +0100568 RTC_DCHECK_EQ(adaptation.validation_id_, adaptation_validation_id_);
Henrik Boström453953c2020-03-14 10:53:57 +0100569 if (adaptation.status() != Adaptation::Status::kValid)
570 return source_restrictor_->source_restrictions();
571 VideoSourceRestrictor restrictor_copy = *source_restrictor_;
572 restrictor_copy.ApplyAdaptationStep(adaptation.step(),
573 EffectiveDegradationPreference());
574 return restrictor_copy.source_restrictions();
575}
576
577ResourceListenerResponse VideoStreamAdapter::ApplyAdaptation(
578 const Adaptation& adaptation) {
579 RTC_DCHECK_EQ(adaptation.validation_id_, adaptation_validation_id_);
580 if (adaptation.status() != Adaptation::Status::kValid) {
581 return ResourceListenerResponse::kNothing;
582 }
Henrik Boströmb0f2e0c2020-03-06 13:32:03 +0100583 // Remember the input pixels and fps of this adaptation. Used to avoid
584 // adapting again before this adaptation has had an effect.
585 last_adaptation_request_.emplace(AdaptationRequest{
Henrik Boström453953c2020-03-14 10:53:57 +0100586 input_pixels_, input_fps_,
587 AdaptationRequest::GetModeFromAdaptationAction(adaptation.step().type)});
Henrik Boström8d9f7502020-03-06 14:51:34 +0100588 // Adapt!
Henrik Boström453953c2020-03-14 10:53:57 +0100589 source_restrictor_->ApplyAdaptationStep(adaptation.step(),
590 EffectiveDegradationPreference());
Henrik Boström8d9f7502020-03-06 14:51:34 +0100591 // In BALANCED, if requested FPS is higher or close to input FPS to the target
592 // we tell the QualityScaler to increase its frequency.
593 // TODO(hbos): Don't have QualityScaler-specific logic here. If the
594 // QualityScaler wants to add special logic depending on what effects
595 // adaptation had, it should listen to changes to the VideoSourceRestrictions
596 // instead.
Henrik Boström453953c2020-03-14 10:53:57 +0100597 if (EffectiveDegradationPreference() == DegradationPreference::BALANCED &&
598 adaptation.step().type == Adaptation::StepType::kDecreaseFrameRate) {
599 absl::optional<int> min_diff = balanced_settings_.MinFpsDiff(input_pixels_);
600 if (min_diff && input_fps_ > 0) {
601 int fps_diff = input_fps_ - adaptation.step().target;
Henrik Boström8d9f7502020-03-06 14:51:34 +0100602 if (fps_diff < min_diff.value()) {
603 return ResourceListenerResponse::kQualityScalerShouldIncreaseFrequency;
604 }
605 }
606 }
607 return ResourceListenerResponse::kNothing;
608}
609
Henrik Boström453953c2020-03-14 10:53:57 +0100610DegradationPreference VideoStreamAdapter::EffectiveDegradationPreference()
611 const {
Henrik Boström8d9f7502020-03-06 14:51:34 +0100612 // Balanced mode for screenshare works via automatic animation detection:
613 // Resolution is capped for fullscreen animated content.
614 // Adapatation is done only via framerate downgrade.
615 // Thus effective degradation preference is MAINTAIN_RESOLUTION.
Henrik Boström453953c2020-03-14 10:53:57 +0100616 return (input_mode_ == VideoInputMode::kScreenshareVideo &&
Henrik Boström8d9f7502020-03-06 14:51:34 +0100617 degradation_preference_ == DegradationPreference::BALANCED)
618 ? DegradationPreference::MAINTAIN_RESOLUTION
619 : degradation_preference_;
Henrik Boströmefbec9a2020-03-06 10:41:25 +0100620}
621
622} // namespace webrtc