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