• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "call/adaptation/video_stream_adapter.h"
12 
13 #include <algorithm>
14 #include <limits>
15 #include <utility>
16 
17 #include "absl/types/optional.h"
18 #include "absl/types/variant.h"
19 #include "api/video/video_adaptation_counters.h"
20 #include "api/video/video_adaptation_reason.h"
21 #include "api/video_codecs/video_encoder.h"
22 #include "call/adaptation/video_source_restrictions.h"
23 #include "call/adaptation/video_stream_input_state.h"
24 #include "rtc_base/checks.h"
25 #include "rtc_base/constructor_magic.h"
26 #include "rtc_base/logging.h"
27 #include "rtc_base/numerics/safe_conversions.h"
28 #include "rtc_base/synchronization/sequence_checker.h"
29 
30 namespace webrtc {
31 
32 const int kMinFrameRateFps = 2;
33 
34 namespace {
35 
36 // For frame rate, the steps we take are 2/3 (down) and 3/2 (up).
GetLowerFrameRateThan(int fps)37 int GetLowerFrameRateThan(int fps) {
38   RTC_DCHECK(fps != std::numeric_limits<int>::max());
39   return (fps * 2) / 3;
40 }
41 // TODO(hbos): Use absl::optional<> instead?
GetHigherFrameRateThan(int fps)42 int GetHigherFrameRateThan(int fps) {
43   return fps != std::numeric_limits<int>::max()
44              ? (fps * 3) / 2
45              : std::numeric_limits<int>::max();
46 }
47 
48 // For resolution, the steps we take are 3/5 (down) and 5/3 (up).
49 // Notice the asymmetry of which restriction property is set depending on if
50 // we are adapting up or down:
51 // - VideoSourceRestrictor::DecreaseResolution() sets the max_pixels_per_frame()
52 //   to the desired target and target_pixels_per_frame() to null.
53 // - VideoSourceRestrictor::IncreaseResolutionTo() sets the
54 //   target_pixels_per_frame() to the desired target, and max_pixels_per_frame()
55 //   is set according to VideoSourceRestrictor::GetIncreasedMaxPixelsWanted().
GetLowerResolutionThan(int pixel_count)56 int GetLowerResolutionThan(int pixel_count) {
57   RTC_DCHECK(pixel_count != std::numeric_limits<int>::max());
58   return (pixel_count * 3) / 5;
59 }
60 
GetIncreasedMaxPixelsWanted(int target_pixels)61 int GetIncreasedMaxPixelsWanted(int target_pixels) {
62   if (target_pixels == std::numeric_limits<int>::max())
63     return std::numeric_limits<int>::max();
64   // When we decrease resolution, we go down to at most 3/5 of current pixels.
65   // Thus to increase resolution, we need 3/5 to get back to where we started.
66   // When going up, the desired max_pixels_per_frame() has to be significantly
67   // higher than the target because the source's native resolutions might not
68   // match the target. We pick 12/5 of the target.
69   //
70   // (This value was historically 4 times the old target, which is (3/5)*4 of
71   // the new target - or 12/5 - assuming the target is adjusted according to
72   // the above steps.)
73   RTC_DCHECK(target_pixels != std::numeric_limits<int>::max());
74   return (target_pixels * 12) / 5;
75 }
76 
CanDecreaseResolutionTo(int target_pixels,const VideoStreamInputState & input_state,const VideoSourceRestrictions & restrictions)77 bool CanDecreaseResolutionTo(int target_pixels,
78                              const VideoStreamInputState& input_state,
79                              const VideoSourceRestrictions& restrictions) {
80   int max_pixels_per_frame =
81       rtc::dchecked_cast<int>(restrictions.max_pixels_per_frame().value_or(
82           std::numeric_limits<int>::max()));
83   return target_pixels < max_pixels_per_frame &&
84          target_pixels >= input_state.min_pixels_per_frame();
85 }
86 
CanIncreaseResolutionTo(int target_pixels,const VideoSourceRestrictions & restrictions)87 bool CanIncreaseResolutionTo(int target_pixels,
88                              const VideoSourceRestrictions& restrictions) {
89   int max_pixels_wanted = GetIncreasedMaxPixelsWanted(target_pixels);
90   int max_pixels_per_frame =
91       rtc::dchecked_cast<int>(restrictions.max_pixels_per_frame().value_or(
92           std::numeric_limits<int>::max()));
93   return max_pixels_wanted > max_pixels_per_frame;
94 }
95 
CanDecreaseFrameRateTo(int max_frame_rate,const VideoSourceRestrictions & restrictions)96 bool CanDecreaseFrameRateTo(int max_frame_rate,
97                             const VideoSourceRestrictions& restrictions) {
98   const int fps_wanted = std::max(kMinFrameRateFps, max_frame_rate);
99   return fps_wanted <
100          rtc::dchecked_cast<int>(restrictions.max_frame_rate().value_or(
101              std::numeric_limits<int>::max()));
102 }
103 
CanIncreaseFrameRateTo(int max_frame_rate,const VideoSourceRestrictions & restrictions)104 bool CanIncreaseFrameRateTo(int max_frame_rate,
105                             const VideoSourceRestrictions& restrictions) {
106   return max_frame_rate >
107          rtc::dchecked_cast<int>(restrictions.max_frame_rate().value_or(
108              std::numeric_limits<int>::max()));
109 }
110 
111 }  // namespace
112 
113 VideoSourceRestrictionsListener::~VideoSourceRestrictionsListener() = default;
114 
FilterRestrictionsByDegradationPreference(VideoSourceRestrictions source_restrictions,DegradationPreference degradation_preference)115 VideoSourceRestrictions FilterRestrictionsByDegradationPreference(
116     VideoSourceRestrictions source_restrictions,
117     DegradationPreference degradation_preference) {
118   switch (degradation_preference) {
119     case DegradationPreference::BALANCED:
120       break;
121     case DegradationPreference::MAINTAIN_FRAMERATE:
122       source_restrictions.set_max_frame_rate(absl::nullopt);
123       break;
124     case DegradationPreference::MAINTAIN_RESOLUTION:
125       source_restrictions.set_max_pixels_per_frame(absl::nullopt);
126       source_restrictions.set_target_pixels_per_frame(absl::nullopt);
127       break;
128     case DegradationPreference::DISABLED:
129       source_restrictions.set_max_pixels_per_frame(absl::nullopt);
130       source_restrictions.set_target_pixels_per_frame(absl::nullopt);
131       source_restrictions.set_max_frame_rate(absl::nullopt);
132   }
133   return source_restrictions;
134 }
135 
136 // TODO(hbos): Use absl::optional<> instead?
GetHigherResolutionThan(int pixel_count)137 int GetHigherResolutionThan(int pixel_count) {
138   return pixel_count != std::numeric_limits<int>::max()
139              ? (pixel_count * 5) / 3
140              : std::numeric_limits<int>::max();
141 }
142 
143 // static
StatusToString(Adaptation::Status status)144 const char* Adaptation::StatusToString(Adaptation::Status status) {
145   switch (status) {
146     case Adaptation::Status::kValid:
147       return "kValid";
148     case Adaptation::Status::kLimitReached:
149       return "kLimitReached";
150     case Adaptation::Status::kAwaitingPreviousAdaptation:
151       return "kAwaitingPreviousAdaptation";
152     case Status::kInsufficientInput:
153       return "kInsufficientInput";
154     case Status::kAdaptationDisabled:
155       return "kAdaptationDisabled";
156     case Status::kRejectedByConstraint:
157       return "kRejectedByConstraint";
158   }
159 }
160 
Adaptation(int validation_id,VideoSourceRestrictions restrictions,VideoAdaptationCounters counters,VideoStreamInputState input_state,bool min_pixel_limit_reached)161 Adaptation::Adaptation(int validation_id,
162                        VideoSourceRestrictions restrictions,
163                        VideoAdaptationCounters counters,
164                        VideoStreamInputState input_state,
165                        bool min_pixel_limit_reached)
166     : validation_id_(validation_id),
167       status_(Status::kValid),
168       min_pixel_limit_reached_(min_pixel_limit_reached),
169       input_state_(std::move(input_state)),
170       restrictions_(std::move(restrictions)),
171       counters_(std::move(counters)) {}
172 
Adaptation(int validation_id,Status invalid_status,VideoStreamInputState input_state,bool min_pixel_limit_reached)173 Adaptation::Adaptation(int validation_id,
174                        Status invalid_status,
175                        VideoStreamInputState input_state,
176                        bool min_pixel_limit_reached)
177     : validation_id_(validation_id),
178       status_(invalid_status),
179       min_pixel_limit_reached_(min_pixel_limit_reached),
180       input_state_(std::move(input_state)) {
181   RTC_DCHECK_NE(status_, Status::kValid);
182 }
183 
status() const184 Adaptation::Status Adaptation::status() const {
185   return status_;
186 }
187 
min_pixel_limit_reached() const188 bool Adaptation::min_pixel_limit_reached() const {
189   return min_pixel_limit_reached_;
190 }
191 
input_state() const192 const VideoStreamInputState& Adaptation::input_state() const {
193   return input_state_;
194 }
195 
restrictions() const196 const VideoSourceRestrictions& Adaptation::restrictions() const {
197   return restrictions_;
198 }
199 
counters() const200 const VideoAdaptationCounters& Adaptation::counters() const {
201   return counters_;
202 }
203 
VideoStreamAdapter(VideoStreamInputStateProvider * input_state_provider)204 VideoStreamAdapter::VideoStreamAdapter(
205     VideoStreamInputStateProvider* input_state_provider)
206     : input_state_provider_(input_state_provider),
207       balanced_settings_(),
208       adaptation_validation_id_(0),
209       degradation_preference_(DegradationPreference::DISABLED),
210       awaiting_frame_size_change_(absl::nullopt),
211       last_video_source_restrictions_() {
212   sequence_checker_.Detach();
213 }
214 
~VideoStreamAdapter()215 VideoStreamAdapter::~VideoStreamAdapter() {
216   RTC_DCHECK(adaptation_listeners_.empty())
217       << "There are listener(s) attached to a VideoStreamAdapter being "
218          "destroyed.";
219   RTC_DCHECK(adaptation_constraints_.empty())
220       << "There are constaint(s) attached to a VideoStreamAdapter being "
221          "destroyed.";
222 }
223 
source_restrictions() const224 VideoSourceRestrictions VideoStreamAdapter::source_restrictions() const {
225   RTC_DCHECK_RUN_ON(&sequence_checker_);
226   return current_restrictions_.restrictions;
227 }
228 
adaptation_counters() const229 const VideoAdaptationCounters& VideoStreamAdapter::adaptation_counters() const {
230   RTC_DCHECK_RUN_ON(&sequence_checker_);
231   return current_restrictions_.counters;
232 }
233 
ClearRestrictions()234 void VideoStreamAdapter::ClearRestrictions() {
235   RTC_DCHECK_RUN_ON(&sequence_checker_);
236   // Invalidate any previously returned Adaptation.
237   RTC_LOG(INFO) << "Resetting restrictions";
238   ++adaptation_validation_id_;
239   current_restrictions_ = {VideoSourceRestrictions(),
240                            VideoAdaptationCounters()};
241   awaiting_frame_size_change_ = absl::nullopt;
242   BroadcastVideoRestrictionsUpdate(input_state_provider_->InputState(),
243                                    nullptr);
244 }
245 
AddRestrictionsListener(VideoSourceRestrictionsListener * restrictions_listener)246 void VideoStreamAdapter::AddRestrictionsListener(
247     VideoSourceRestrictionsListener* restrictions_listener) {
248   RTC_DCHECK_RUN_ON(&sequence_checker_);
249   RTC_DCHECK(std::find(restrictions_listeners_.begin(),
250                        restrictions_listeners_.end(),
251                        restrictions_listener) == restrictions_listeners_.end());
252   restrictions_listeners_.push_back(restrictions_listener);
253 }
254 
RemoveRestrictionsListener(VideoSourceRestrictionsListener * restrictions_listener)255 void VideoStreamAdapter::RemoveRestrictionsListener(
256     VideoSourceRestrictionsListener* restrictions_listener) {
257   RTC_DCHECK_RUN_ON(&sequence_checker_);
258   auto it = std::find(restrictions_listeners_.begin(),
259                       restrictions_listeners_.end(), restrictions_listener);
260   RTC_DCHECK(it != restrictions_listeners_.end());
261   restrictions_listeners_.erase(it);
262 }
263 
AddAdaptationListener(AdaptationListener * adaptation_listener)264 void VideoStreamAdapter::AddAdaptationListener(
265     AdaptationListener* adaptation_listener) {
266   RTC_DCHECK_RUN_ON(&sequence_checker_);
267   RTC_DCHECK(std::find(adaptation_listeners_.begin(),
268                        adaptation_listeners_.end(),
269                        adaptation_listener) == adaptation_listeners_.end());
270   adaptation_listeners_.push_back(adaptation_listener);
271 }
272 
RemoveAdaptationListener(AdaptationListener * adaptation_listener)273 void VideoStreamAdapter::RemoveAdaptationListener(
274     AdaptationListener* adaptation_listener) {
275   RTC_DCHECK_RUN_ON(&sequence_checker_);
276   auto it = std::find(adaptation_listeners_.begin(),
277                       adaptation_listeners_.end(), adaptation_listener);
278   RTC_DCHECK(it != adaptation_listeners_.end());
279   adaptation_listeners_.erase(it);
280 }
281 
AddAdaptationConstraint(AdaptationConstraint * adaptation_constraint)282 void VideoStreamAdapter::AddAdaptationConstraint(
283     AdaptationConstraint* adaptation_constraint) {
284   RTC_DCHECK_RUN_ON(&sequence_checker_);
285   RTC_DCHECK(std::find(adaptation_constraints_.begin(),
286                        adaptation_constraints_.end(),
287                        adaptation_constraint) == adaptation_constraints_.end());
288   adaptation_constraints_.push_back(adaptation_constraint);
289 }
290 
RemoveAdaptationConstraint(AdaptationConstraint * adaptation_constraint)291 void VideoStreamAdapter::RemoveAdaptationConstraint(
292     AdaptationConstraint* adaptation_constraint) {
293   RTC_DCHECK_RUN_ON(&sequence_checker_);
294   auto it = std::find(adaptation_constraints_.begin(),
295                       adaptation_constraints_.end(), adaptation_constraint);
296   RTC_DCHECK(it != adaptation_constraints_.end());
297   adaptation_constraints_.erase(it);
298 }
299 
SetDegradationPreference(DegradationPreference degradation_preference)300 void VideoStreamAdapter::SetDegradationPreference(
301     DegradationPreference degradation_preference) {
302   RTC_DCHECK_RUN_ON(&sequence_checker_);
303   if (degradation_preference_ == degradation_preference)
304     return;
305   // Invalidate any previously returned Adaptation.
306   ++adaptation_validation_id_;
307   bool balanced_switch =
308       degradation_preference == DegradationPreference::BALANCED ||
309       degradation_preference_ == DegradationPreference::BALANCED;
310   degradation_preference_ = degradation_preference;
311   if (balanced_switch) {
312     // ClearRestrictions() calls BroadcastVideoRestrictionsUpdate(nullptr).
313     ClearRestrictions();
314   } else {
315     BroadcastVideoRestrictionsUpdate(input_state_provider_->InputState(),
316                                      nullptr);
317   }
318 }
319 
320 struct VideoStreamAdapter::RestrictionsOrStateVisitor {
operator ()webrtc::VideoStreamAdapter::RestrictionsOrStateVisitor321   Adaptation operator()(const RestrictionsWithCounters& r) const {
322     return Adaptation(adaptation_validation_id, r.restrictions, r.counters,
323                       input_state, min_pixel_limit_reached());
324   }
operator ()webrtc::VideoStreamAdapter::RestrictionsOrStateVisitor325   Adaptation operator()(const Adaptation::Status& status) const {
326     RTC_DCHECK_NE(status, Adaptation::Status::kValid);
327     return Adaptation(adaptation_validation_id, status, input_state,
328                       min_pixel_limit_reached());
329   }
min_pixel_limit_reachedwebrtc::VideoStreamAdapter::RestrictionsOrStateVisitor330   bool min_pixel_limit_reached() const {
331     return input_state.frame_size_pixels().has_value() &&
332            GetLowerResolutionThan(input_state.frame_size_pixels().value()) <
333                input_state.min_pixels_per_frame();
334   }
335 
336   const int adaptation_validation_id;
337   const VideoStreamInputState& input_state;
338 };
339 
RestrictionsOrStateToAdaptation(VideoStreamAdapter::RestrictionsOrState step_or_state,const VideoStreamInputState & input_state) const340 Adaptation VideoStreamAdapter::RestrictionsOrStateToAdaptation(
341     VideoStreamAdapter::RestrictionsOrState step_or_state,
342     const VideoStreamInputState& input_state) const {
343   RTC_DCHECK(!step_or_state.valueless_by_exception());
344   return absl::visit(
345       RestrictionsOrStateVisitor{adaptation_validation_id_, input_state},
346       step_or_state);
347 }
348 
GetAdaptationUp(const VideoStreamInputState & input_state,rtc::scoped_refptr<Resource> resource) const349 Adaptation VideoStreamAdapter::GetAdaptationUp(
350     const VideoStreamInputState& input_state,
351     rtc::scoped_refptr<Resource> resource) const {
352   RestrictionsOrState step = GetAdaptationUpStep(input_state);
353   // If an adaptation proposed, check with the constraints that it is ok.
354   if (absl::holds_alternative<RestrictionsWithCounters>(step)) {
355     RestrictionsWithCounters restrictions =
356         absl::get<RestrictionsWithCounters>(step);
357     for (const auto* constraint : adaptation_constraints_) {
358       if (!constraint->IsAdaptationUpAllowed(
359               input_state, current_restrictions_.restrictions,
360               restrictions.restrictions, resource)) {
361         RTC_LOG(INFO) << "Not adapting up because constraint \""
362                       << constraint->Name() << "\" disallowed it";
363         step = Adaptation::Status::kRejectedByConstraint;
364       }
365     }
366   }
367   return RestrictionsOrStateToAdaptation(step, input_state);
368 }
369 
GetAdaptationUp(rtc::scoped_refptr<Resource> resource)370 Adaptation VideoStreamAdapter::GetAdaptationUp(
371     rtc::scoped_refptr<Resource> resource) {
372   RTC_DCHECK_RUN_ON(&sequence_checker_);
373   RTC_DCHECK(resource);
374   VideoStreamInputState input_state = input_state_provider_->InputState();
375   ++adaptation_validation_id_;
376   Adaptation adaptation = GetAdaptationUp(input_state, resource);
377   return adaptation;
378 }
379 
GetAdaptationUpStep(const VideoStreamInputState & input_state) const380 VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::GetAdaptationUpStep(
381     const VideoStreamInputState& input_state) const {
382   if (!HasSufficientInputForAdaptation(input_state)) {
383     return Adaptation::Status::kInsufficientInput;
384   }
385   // Don't adapt if we're awaiting a previous adaptation to have an effect.
386   if (awaiting_frame_size_change_ &&
387       awaiting_frame_size_change_->pixels_increased &&
388       degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
389       input_state.frame_size_pixels().value() <=
390           awaiting_frame_size_change_->frame_size_pixels) {
391     return Adaptation::Status::kAwaitingPreviousAdaptation;
392   }
393 
394   // Maybe propose targets based on degradation preference.
395   switch (degradation_preference_) {
396     case DegradationPreference::BALANCED: {
397       // Attempt to increase target frame rate.
398       RestrictionsOrState increase_frame_rate =
399           IncreaseFramerate(input_state, current_restrictions_);
400       if (absl::holds_alternative<RestrictionsWithCounters>(
401               increase_frame_rate)) {
402         return increase_frame_rate;
403       }
404       // else, increase resolution.
405       ABSL_FALLTHROUGH_INTENDED;
406     }
407     case DegradationPreference::MAINTAIN_FRAMERATE: {
408       // Attempt to increase pixel count.
409       return IncreaseResolution(input_state, current_restrictions_);
410     }
411     case DegradationPreference::MAINTAIN_RESOLUTION: {
412       // Scale up framerate.
413       return IncreaseFramerate(input_state, current_restrictions_);
414     }
415     case DegradationPreference::DISABLED:
416       return Adaptation::Status::kAdaptationDisabled;
417   }
418 }
419 
GetAdaptationDown()420 Adaptation VideoStreamAdapter::GetAdaptationDown() {
421   RTC_DCHECK_RUN_ON(&sequence_checker_);
422   VideoStreamInputState input_state = input_state_provider_->InputState();
423   ++adaptation_validation_id_;
424   return RestrictionsOrStateToAdaptation(GetAdaptationDownStep(input_state),
425                                          input_state);
426 }
427 
428 VideoStreamAdapter::RestrictionsOrState
GetAdaptationDownStep(const VideoStreamInputState & input_state) const429 VideoStreamAdapter::GetAdaptationDownStep(
430     const VideoStreamInputState& input_state) const {
431   if (!HasSufficientInputForAdaptation(input_state)) {
432     return Adaptation::Status::kInsufficientInput;
433   }
434   // Don't adapt if we're awaiting a previous adaptation to have an effect or
435   // if we switched degradation preference.
436   if (awaiting_frame_size_change_ &&
437       !awaiting_frame_size_change_->pixels_increased &&
438       degradation_preference_ == DegradationPreference::MAINTAIN_FRAMERATE &&
439       input_state.frame_size_pixels().value() >=
440           awaiting_frame_size_change_->frame_size_pixels) {
441     return Adaptation::Status::kAwaitingPreviousAdaptation;
442   }
443   // Maybe propose targets based on degradation preference.
444   switch (degradation_preference_) {
445     case DegradationPreference::BALANCED: {
446       // Try scale down framerate, if lower.
447       RestrictionsOrState decrease_frame_rate =
448           DecreaseFramerate(input_state, current_restrictions_);
449       if (absl::holds_alternative<RestrictionsWithCounters>(
450               decrease_frame_rate)) {
451         return decrease_frame_rate;
452       }
453       // else, decrease resolution.
454       ABSL_FALLTHROUGH_INTENDED;
455     }
456     case DegradationPreference::MAINTAIN_FRAMERATE: {
457       return DecreaseResolution(input_state, current_restrictions_);
458     }
459     case DegradationPreference::MAINTAIN_RESOLUTION: {
460       return DecreaseFramerate(input_state, current_restrictions_);
461     }
462     case DegradationPreference::DISABLED:
463       return Adaptation::Status::kAdaptationDisabled;
464   }
465 }
466 
DecreaseResolution(const VideoStreamInputState & input_state,const RestrictionsWithCounters & current_restrictions)467 VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::DecreaseResolution(
468     const VideoStreamInputState& input_state,
469     const RestrictionsWithCounters& current_restrictions) {
470   int target_pixels =
471       GetLowerResolutionThan(input_state.frame_size_pixels().value());
472   if (!CanDecreaseResolutionTo(target_pixels, input_state,
473                                current_restrictions.restrictions)) {
474     return Adaptation::Status::kLimitReached;
475   }
476   RestrictionsWithCounters new_restrictions = current_restrictions;
477   RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: " << target_pixels;
478   new_restrictions.restrictions.set_max_pixels_per_frame(
479       target_pixels != std::numeric_limits<int>::max()
480           ? absl::optional<size_t>(target_pixels)
481           : absl::nullopt);
482   new_restrictions.restrictions.set_target_pixels_per_frame(absl::nullopt);
483   ++new_restrictions.counters.resolution_adaptations;
484   return new_restrictions;
485 }
486 
DecreaseFramerate(const VideoStreamInputState & input_state,const RestrictionsWithCounters & current_restrictions) const487 VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::DecreaseFramerate(
488     const VideoStreamInputState& input_state,
489     const RestrictionsWithCounters& current_restrictions) const {
490   int max_frame_rate;
491   if (degradation_preference_ == DegradationPreference::MAINTAIN_RESOLUTION) {
492     max_frame_rate = GetLowerFrameRateThan(input_state.frames_per_second());
493   } else if (degradation_preference_ == DegradationPreference::BALANCED) {
494     max_frame_rate =
495         balanced_settings_.MinFps(input_state.video_codec_type(),
496                                   input_state.frame_size_pixels().value());
497   } else {
498     RTC_NOTREACHED();
499     max_frame_rate = GetLowerFrameRateThan(input_state.frames_per_second());
500   }
501   if (!CanDecreaseFrameRateTo(max_frame_rate,
502                               current_restrictions.restrictions)) {
503     return Adaptation::Status::kLimitReached;
504   }
505   RestrictionsWithCounters new_restrictions = current_restrictions;
506   max_frame_rate = std::max(kMinFrameRateFps, max_frame_rate);
507   RTC_LOG(LS_INFO) << "Scaling down framerate: " << max_frame_rate;
508   new_restrictions.restrictions.set_max_frame_rate(
509       max_frame_rate != std::numeric_limits<int>::max()
510           ? absl::optional<double>(max_frame_rate)
511           : absl::nullopt);
512   ++new_restrictions.counters.fps_adaptations;
513   return new_restrictions;
514 }
515 
IncreaseResolution(const VideoStreamInputState & input_state,const RestrictionsWithCounters & current_restrictions)516 VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::IncreaseResolution(
517     const VideoStreamInputState& input_state,
518     const RestrictionsWithCounters& current_restrictions) {
519   int target_pixels = input_state.frame_size_pixels().value();
520   if (current_restrictions.counters.resolution_adaptations == 1) {
521     RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting.";
522     target_pixels = std::numeric_limits<int>::max();
523   }
524   target_pixels = GetHigherResolutionThan(target_pixels);
525   if (!CanIncreaseResolutionTo(target_pixels,
526                                current_restrictions.restrictions)) {
527     return Adaptation::Status::kLimitReached;
528   }
529   int max_pixels_wanted = GetIncreasedMaxPixelsWanted(target_pixels);
530   RestrictionsWithCounters new_restrictions = current_restrictions;
531   RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: "
532                    << max_pixels_wanted;
533   new_restrictions.restrictions.set_max_pixels_per_frame(
534       max_pixels_wanted != std::numeric_limits<int>::max()
535           ? absl::optional<size_t>(max_pixels_wanted)
536           : absl::nullopt);
537   new_restrictions.restrictions.set_target_pixels_per_frame(
538       max_pixels_wanted != std::numeric_limits<int>::max()
539           ? absl::optional<size_t>(target_pixels)
540           : absl::nullopt);
541   --new_restrictions.counters.resolution_adaptations;
542   RTC_DCHECK_GE(new_restrictions.counters.resolution_adaptations, 0);
543   return new_restrictions;
544 }
545 
IncreaseFramerate(const VideoStreamInputState & input_state,const RestrictionsWithCounters & current_restrictions) const546 VideoStreamAdapter::RestrictionsOrState VideoStreamAdapter::IncreaseFramerate(
547     const VideoStreamInputState& input_state,
548     const RestrictionsWithCounters& current_restrictions) const {
549   int max_frame_rate;
550   if (degradation_preference_ == DegradationPreference::MAINTAIN_RESOLUTION) {
551     max_frame_rate = GetHigherFrameRateThan(input_state.frames_per_second());
552   } else if (degradation_preference_ == DegradationPreference::BALANCED) {
553     max_frame_rate =
554         balanced_settings_.MaxFps(input_state.video_codec_type(),
555                                   input_state.frame_size_pixels().value());
556     // In BALANCED, the max_frame_rate must be checked before proceeding. This
557     // is because the MaxFps might be the current Fps and so the balanced
558     // settings may want to scale up the resolution.=
559     if (!CanIncreaseFrameRateTo(max_frame_rate,
560                                 current_restrictions.restrictions)) {
561       return Adaptation::Status::kLimitReached;
562     }
563   } else {
564     RTC_NOTREACHED();
565     max_frame_rate = GetHigherFrameRateThan(input_state.frames_per_second());
566   }
567   if (current_restrictions.counters.fps_adaptations == 1) {
568     RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting.";
569     max_frame_rate = std::numeric_limits<int>::max();
570   }
571   if (!CanIncreaseFrameRateTo(max_frame_rate,
572                               current_restrictions.restrictions)) {
573     return Adaptation::Status::kLimitReached;
574   }
575   RTC_LOG(LS_INFO) << "Scaling up framerate: " << max_frame_rate;
576   RestrictionsWithCounters new_restrictions = current_restrictions;
577   new_restrictions.restrictions.set_max_frame_rate(
578       max_frame_rate != std::numeric_limits<int>::max()
579           ? absl::optional<double>(max_frame_rate)
580           : absl::nullopt);
581   --new_restrictions.counters.fps_adaptations;
582   RTC_DCHECK_GE(new_restrictions.counters.fps_adaptations, 0);
583   return new_restrictions;
584 }
585 
GetAdaptDownResolution()586 Adaptation VideoStreamAdapter::GetAdaptDownResolution() {
587   RTC_DCHECK_RUN_ON(&sequence_checker_);
588   VideoStreamInputState input_state = input_state_provider_->InputState();
589   switch (degradation_preference_) {
590     case DegradationPreference::DISABLED:
591       return RestrictionsOrStateToAdaptation(
592           Adaptation::Status::kAdaptationDisabled, input_state);
593     case DegradationPreference::MAINTAIN_RESOLUTION:
594       return RestrictionsOrStateToAdaptation(Adaptation::Status::kLimitReached,
595                                              input_state);
596     case DegradationPreference::MAINTAIN_FRAMERATE:
597       return GetAdaptationDown();
598     case DegradationPreference::BALANCED: {
599       return RestrictionsOrStateToAdaptation(
600           GetAdaptDownResolutionStepForBalanced(input_state), input_state);
601     }
602     default:
603       RTC_NOTREACHED();
604   }
605 }
606 
607 VideoStreamAdapter::RestrictionsOrState
GetAdaptDownResolutionStepForBalanced(const VideoStreamInputState & input_state) const608 VideoStreamAdapter::GetAdaptDownResolutionStepForBalanced(
609     const VideoStreamInputState& input_state) const {
610   // Adapt twice if the first adaptation did not decrease resolution.
611   auto first_step = GetAdaptationDownStep(input_state);
612   if (!absl::holds_alternative<RestrictionsWithCounters>(first_step)) {
613     return first_step;
614   }
615   auto first_restrictions = absl::get<RestrictionsWithCounters>(first_step);
616   if (first_restrictions.counters.resolution_adaptations >
617       current_restrictions_.counters.resolution_adaptations) {
618     return first_step;
619   }
620   // We didn't decrease resolution so force it; amend a resolution resuction
621   // to the existing framerate reduction in |first_restrictions|.
622   auto second_step = DecreaseResolution(input_state, first_restrictions);
623   if (absl::holds_alternative<RestrictionsWithCounters>(second_step)) {
624     return second_step;
625   }
626   // If the second step was not successful then settle for the first one.
627   return first_step;
628 }
629 
ApplyAdaptation(const Adaptation & adaptation,rtc::scoped_refptr<Resource> resource)630 void VideoStreamAdapter::ApplyAdaptation(
631     const Adaptation& adaptation,
632     rtc::scoped_refptr<Resource> resource) {
633   RTC_DCHECK_RUN_ON(&sequence_checker_);
634   RTC_DCHECK_EQ(adaptation.validation_id_, adaptation_validation_id_);
635   if (adaptation.status() != Adaptation::Status::kValid)
636     return;
637   // Remember the input pixels and fps of this adaptation. Used to avoid
638   // adapting again before this adaptation has had an effect.
639   if (DidIncreaseResolution(current_restrictions_.restrictions,
640                             adaptation.restrictions())) {
641     awaiting_frame_size_change_.emplace(
642         true, adaptation.input_state().frame_size_pixels().value());
643   } else if (DidDecreaseResolution(current_restrictions_.restrictions,
644                                    adaptation.restrictions())) {
645     awaiting_frame_size_change_.emplace(
646         false, adaptation.input_state().frame_size_pixels().value());
647   } else {
648     awaiting_frame_size_change_ = absl::nullopt;
649   }
650   current_restrictions_ = {adaptation.restrictions(), adaptation.counters()};
651   BroadcastVideoRestrictionsUpdate(adaptation.input_state(), resource);
652 }
653 
GetAdaptationTo(const VideoAdaptationCounters & counters,const VideoSourceRestrictions & restrictions)654 Adaptation VideoStreamAdapter::GetAdaptationTo(
655     const VideoAdaptationCounters& counters,
656     const VideoSourceRestrictions& restrictions) {
657   // Adapts up/down from the current levels so counters are equal.
658   RTC_DCHECK_RUN_ON(&sequence_checker_);
659   VideoStreamInputState input_state = input_state_provider_->InputState();
660   return Adaptation(adaptation_validation_id_, restrictions, counters,
661                     input_state, false);
662 }
663 
BroadcastVideoRestrictionsUpdate(const VideoStreamInputState & input_state,const rtc::scoped_refptr<Resource> & resource)664 void VideoStreamAdapter::BroadcastVideoRestrictionsUpdate(
665     const VideoStreamInputState& input_state,
666     const rtc::scoped_refptr<Resource>& resource) {
667   RTC_DCHECK_RUN_ON(&sequence_checker_);
668   VideoSourceRestrictions filtered = FilterRestrictionsByDegradationPreference(
669       source_restrictions(), degradation_preference_);
670   if (last_filtered_restrictions_ == filtered) {
671     return;
672   }
673   for (auto* restrictions_listener : restrictions_listeners_) {
674     restrictions_listener->OnVideoSourceRestrictionsUpdated(
675         filtered, current_restrictions_.counters, resource,
676         source_restrictions());
677   }
678   for (auto* adaptation_listener : adaptation_listeners_) {
679     adaptation_listener->OnAdaptationApplied(
680         input_state, last_video_source_restrictions_,
681         current_restrictions_.restrictions, resource);
682   }
683   last_video_source_restrictions_ = current_restrictions_.restrictions;
684   last_filtered_restrictions_ = filtered;
685 }
686 
HasSufficientInputForAdaptation(const VideoStreamInputState & input_state) const687 bool VideoStreamAdapter::HasSufficientInputForAdaptation(
688     const VideoStreamInputState& input_state) const {
689   return input_state.HasInputFrameSizeAndFramesPerSecond() &&
690          (degradation_preference_ !=
691               DegradationPreference::MAINTAIN_RESOLUTION ||
692           input_state.frames_per_second() >= kMinFrameRateFps);
693 }
694 
AwaitingFrameSizeChange(bool pixels_increased,int frame_size_pixels)695 VideoStreamAdapter::AwaitingFrameSizeChange::AwaitingFrameSizeChange(
696     bool pixels_increased,
697     int frame_size_pixels)
698     : pixels_increased(pixels_increased),
699       frame_size_pixels(frame_size_pixels) {}
700 
701 }  // namespace webrtc
702