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