tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

video_stream_adapter.cc (31818B)


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