Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019 The WebRTC Project Authors. All rights reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "video/overuse_frame_detector_resource_adaptation_module.h" |
| 12 | |
| 13 | #include <algorithm> |
| 14 | #include <limits> |
| 15 | #include <memory> |
| 16 | #include <string> |
| 17 | #include <utility> |
| 18 | |
| 19 | #include "absl/algorithm/container.h" |
| 20 | #include "api/video/video_source_interface.h" |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 21 | #include "call/adaptation/video_source_restrictions.h" |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 22 | #include "rtc_base/logging.h" |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 23 | #include "rtc_base/numerics/safe_conversions.h" |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 24 | #include "rtc_base/strings/string_builder.h" |
| 25 | #include "rtc_base/system/fallthrough.h" |
| 26 | #include "video/video_stream_encoder.h" |
| 27 | |
| 28 | namespace webrtc { |
| 29 | |
| 30 | namespace { |
| 31 | |
| 32 | const int kMinFramerateFps = 2; |
| 33 | |
| 34 | bool IsResolutionScalingEnabled(DegradationPreference degradation_preference) { |
| 35 | return degradation_preference == DegradationPreference::MAINTAIN_FRAMERATE || |
| 36 | degradation_preference == DegradationPreference::BALANCED; |
| 37 | } |
| 38 | |
| 39 | bool IsFramerateScalingEnabled(DegradationPreference degradation_preference) { |
| 40 | return degradation_preference == DegradationPreference::MAINTAIN_RESOLUTION || |
| 41 | degradation_preference == DegradationPreference::BALANCED; |
| 42 | } |
| 43 | |
| 44 | } // namespace |
| 45 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 46 | // VideoSourceRestrictor is responsible for keeping track of current |
| 47 | // VideoSourceRestrictions and how to modify them in response to adapting up or |
| 48 | // down. It is not reponsible for determining when we should adapt up or down - |
| 49 | // for that, see OveruseFrameDetectorResourceAdaptationModule::AdaptUp() and |
| 50 | // AdaptDown() - only how to modify the source/sink restrictions when this |
| 51 | // happens. Note that it is also not responsible for reconfigruring the |
| 52 | // source/sink, it is only a keeper of desired restrictions. |
| 53 | // |
| 54 | // Thread safety is ensured between SetHasInputVideoAndDegradationPreference() |
| 55 | // calls on the worker thread and adaptation logic on the encoder task queue |
| 56 | // using a lock. |
| 57 | class OveruseFrameDetectorResourceAdaptationModule::VideoSourceRestrictor { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 58 | public: |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 59 | explicit VideoSourceRestrictor( |
| 60 | VideoSourceSinkController* video_source_sink_controller) |
| 61 | : video_source_sink_controller_(video_source_sink_controller), |
| 62 | has_input_video_(false), |
| 63 | degradation_preference_(DegradationPreference::DISABLED) {} |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 64 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 65 | VideoSourceRestrictions source_restrictions() { |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 66 | rtc::CritScope lock(&crit_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 67 | return source_restrictions_; |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 68 | } |
| 69 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 70 | // Inform the restrictor of new source status and degradation preference. |
| 71 | // TODO(hbos): Can this be moved to the encoder queue? If so, the |crit_| lock |
| 72 | // can be removed and we only need a sequence checker. |
| 73 | void SetHasInputVideoAndDegradationPreference( |
| 74 | bool has_input_video, |
| 75 | DegradationPreference degradation_preference) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 76 | // Called on libjingle's worker thread. |
| 77 | RTC_DCHECK_RUN_ON(&main_checker_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 78 | rtc::CritScope lock(&crit_); |
| 79 | has_input_video_ = has_input_video; |
| 80 | degradation_preference_ = degradation_preference; |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 81 | } |
| 82 | |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 83 | // Informs the sink of the new source settings. |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 84 | // TODO(https://crbug.com/webrtc/11222): Handle all sink updates in |
| 85 | // video_stream_encoder.cc. This method is only used when setting the |
| 86 | // degradation preference such that it moves in or out of the "balanced" |
| 87 | // state, or when clearing all counters. When moving the remaining degradation |
| 88 | // preference logic inside the VideoSourceSinkController to here, stop |
| 89 | // explicitly setting the controller's restrictions and instead inform the |
| 90 | // VideoStreamEncoder of updated restrictions using |
| 91 | // OnVideoSourceRestrictionsUpdated(). |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 92 | void ResetPixelFpsCount() { |
| 93 | rtc::CritScope lock(&crit_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 94 | // Clear all restrictions. |
| 95 | source_restrictions_ = VideoSourceRestrictions(); |
| 96 | video_source_sink_controller_->SetRestrictions(source_restrictions_); |
| 97 | video_source_sink_controller_->PushSourceSinkSettings(); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 98 | } |
| 99 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 100 | // Updates the source_restrictions(). The source/sink has to be informed of |
| 101 | // this separately. |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 102 | bool RequestResolutionLowerThan(int pixel_count, |
| 103 | int min_pixels_per_frame, |
| 104 | bool* min_pixels_reached) { |
| 105 | // Called on the encoder task queue. |
| 106 | rtc::CritScope lock(&crit_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 107 | if (!has_input_video_ || |
| 108 | !IsResolutionScalingEnabled(degradation_preference_)) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 109 | // This can happen since |degradation_preference_| is set on libjingle's |
| 110 | // worker thread but the adaptation is done on the encoder task queue. |
| 111 | return false; |
| 112 | } |
| 113 | // The input video frame size will have a resolution less than or equal to |
| 114 | // |max_pixel_count| depending on how the source can scale the frame size. |
| 115 | const int pixels_wanted = (pixel_count * 3) / 5; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 116 | if (pixels_wanted >= |
| 117 | rtc::dchecked_cast<int>( |
| 118 | source_restrictions_.max_pixels_per_frame().value_or( |
| 119 | std::numeric_limits<int>::max()))) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 120 | return false; |
| 121 | } |
| 122 | if (pixels_wanted < min_pixels_per_frame) { |
| 123 | *min_pixels_reached = true; |
| 124 | return false; |
| 125 | } |
| 126 | RTC_LOG(LS_INFO) << "Scaling down resolution, max pixels: " |
| 127 | << pixels_wanted; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 128 | source_restrictions_.set_max_pixels_per_frame( |
| 129 | pixels_wanted != std::numeric_limits<int>::max() |
| 130 | ? absl::optional<size_t>(pixels_wanted) |
| 131 | : absl::nullopt); |
| 132 | source_restrictions_.set_target_pixels_per_frame(absl::nullopt); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 133 | return true; |
| 134 | } |
| 135 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 136 | // Updates the source_restrictions(). The source/sink has to be informed of |
| 137 | // this separately. |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 138 | int RequestFramerateLowerThan(int fps) { |
| 139 | // Called on the encoder task queue. |
| 140 | // The input video frame rate will be scaled down to 2/3, rounding down. |
| 141 | int framerate_wanted = (fps * 2) / 3; |
| 142 | return RestrictFramerate(framerate_wanted) ? framerate_wanted : -1; |
| 143 | } |
| 144 | |
| 145 | int GetHigherResolutionThan(int pixel_count) const { |
| 146 | // On step down we request at most 3/5 the pixel count of the previous |
| 147 | // resolution, so in order to take "one step up" we request a resolution |
| 148 | // as close as possible to 5/3 of the current resolution. The actual pixel |
| 149 | // count selected depends on the capabilities of the source. In order to |
| 150 | // not take a too large step up, we cap the requested pixel count to be at |
| 151 | // most four time the current number of pixels. |
| 152 | return (pixel_count * 5) / 3; |
| 153 | } |
| 154 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 155 | // Updates the source_restrictions(). The source/sink has to be informed of |
| 156 | // this separately. |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 157 | bool RequestHigherResolutionThan(int pixel_count) { |
| 158 | // Called on the encoder task queue. |
| 159 | rtc::CritScope lock(&crit_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 160 | if (!has_input_video_ || |
| 161 | !IsResolutionScalingEnabled(degradation_preference_)) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 162 | // This can happen since |degradation_preference_| is set on libjingle's |
| 163 | // worker thread but the adaptation is done on the encoder task queue. |
| 164 | return false; |
| 165 | } |
| 166 | int max_pixels_wanted = pixel_count; |
| 167 | if (max_pixels_wanted != std::numeric_limits<int>::max()) |
| 168 | max_pixels_wanted = pixel_count * 4; |
| 169 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 170 | if (max_pixels_wanted <= |
| 171 | rtc::dchecked_cast<int>( |
| 172 | source_restrictions_.max_pixels_per_frame().value_or( |
| 173 | std::numeric_limits<int>::max()))) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 174 | return false; |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 175 | } |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 176 | |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 177 | RTC_LOG(LS_INFO) << "Scaling up resolution, max pixels: " |
| 178 | << max_pixels_wanted; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 179 | source_restrictions_.set_max_pixels_per_frame( |
| 180 | max_pixels_wanted != std::numeric_limits<int>::max() |
| 181 | ? absl::optional<size_t>(max_pixels_wanted) |
| 182 | : absl::nullopt); |
| 183 | source_restrictions_.set_target_pixels_per_frame( |
| 184 | max_pixels_wanted != std::numeric_limits<int>::max() |
| 185 | ? absl::optional<size_t>(GetHigherResolutionThan(pixel_count)) |
| 186 | : absl::nullopt); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 187 | return true; |
| 188 | } |
| 189 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 190 | // Updates the source_restrictions(). The source/sink has to be informed of |
| 191 | // this separately. |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 192 | // Request upgrade in framerate. Returns the new requested frame, or -1 if |
| 193 | // no change requested. Note that maxint may be returned if limits due to |
| 194 | // adaptation requests are removed completely. In that case, consider |
| 195 | // |max_framerate_| to be the current limit (assuming the capturer complies). |
| 196 | int RequestHigherFramerateThan(int fps) { |
| 197 | // Called on the encoder task queue. |
| 198 | // The input frame rate will be scaled up to the last step, with rounding. |
| 199 | int framerate_wanted = fps; |
| 200 | if (fps != std::numeric_limits<int>::max()) |
| 201 | framerate_wanted = (fps * 3) / 2; |
| 202 | |
| 203 | return IncreaseFramerate(framerate_wanted) ? framerate_wanted : -1; |
| 204 | } |
| 205 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 206 | // Updates the source_restrictions(). The source/sink has to be informed of |
| 207 | // this separately. |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 208 | bool RestrictFramerate(int fps) { |
| 209 | // Called on the encoder task queue. |
| 210 | rtc::CritScope lock(&crit_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 211 | if (!has_input_video_ || |
| 212 | !IsFramerateScalingEnabled(degradation_preference_)) |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 213 | return false; |
| 214 | |
| 215 | const int fps_wanted = std::max(kMinFramerateFps, fps); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 216 | if (fps_wanted >= |
| 217 | rtc::dchecked_cast<int>(source_restrictions_.max_frame_rate().value_or( |
| 218 | std::numeric_limits<int>::max()))) |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 219 | return false; |
| 220 | |
| 221 | RTC_LOG(LS_INFO) << "Scaling down framerate: " << fps_wanted; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 222 | source_restrictions_.set_max_frame_rate( |
| 223 | fps_wanted != std::numeric_limits<int>::max() |
| 224 | ? absl::optional<double>(fps_wanted) |
| 225 | : absl::nullopt); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 226 | return true; |
| 227 | } |
| 228 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 229 | // Updates the source_restrictions(). The source/sink has to be informed of |
| 230 | // this separately. |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 231 | bool IncreaseFramerate(int fps) { |
| 232 | // Called on the encoder task queue. |
| 233 | rtc::CritScope lock(&crit_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 234 | if (!has_input_video_ || |
| 235 | !IsFramerateScalingEnabled(degradation_preference_)) |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 236 | return false; |
| 237 | |
| 238 | const int fps_wanted = std::max(kMinFramerateFps, fps); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 239 | if (fps_wanted <= |
| 240 | rtc::dchecked_cast<int>(source_restrictions_.max_frame_rate().value_or( |
| 241 | std::numeric_limits<int>::max()))) |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 242 | return false; |
| 243 | |
| 244 | RTC_LOG(LS_INFO) << "Scaling up framerate: " << fps_wanted; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 245 | source_restrictions_.set_max_frame_rate( |
| 246 | fps_wanted != std::numeric_limits<int>::max() |
| 247 | ? absl::optional<double>(fps_wanted) |
| 248 | : absl::nullopt); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 249 | return true; |
| 250 | } |
| 251 | |
| 252 | private: |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 253 | rtc::CriticalSection crit_; |
| 254 | SequenceChecker main_checker_; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 255 | VideoSourceSinkController* const video_source_sink_controller_; |
| 256 | VideoSourceRestrictions source_restrictions_ RTC_GUARDED_BY(&crit_); |
| 257 | bool has_input_video_ RTC_GUARDED_BY(&crit_); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 258 | DegradationPreference degradation_preference_ RTC_GUARDED_BY(&crit_); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 259 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 260 | RTC_DISALLOW_COPY_AND_ASSIGN(VideoSourceRestrictor); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 261 | }; |
| 262 | |
| 263 | // Class holding adaptation information. |
| 264 | OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::AdaptCounter() { |
| 265 | fps_counters_.resize(kScaleReasonSize); |
| 266 | resolution_counters_.resize(kScaleReasonSize); |
| 267 | static_assert(kScaleReasonSize == 2, "Update MoveCount."); |
| 268 | } |
| 269 | |
| 270 | OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::~AdaptCounter() {} |
| 271 | |
| 272 | std::string |
| 273 | OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString() const { |
| 274 | rtc::StringBuilder ss; |
| 275 | ss << "Downgrade counts: fps: {" << ToString(fps_counters_); |
| 276 | ss << "}, resolution: {" << ToString(resolution_counters_) << "}"; |
| 277 | return ss.Release(); |
| 278 | } |
| 279 | |
| 280 | VideoStreamEncoderObserver::AdaptationSteps |
| 281 | OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Counts( |
| 282 | int reason) const { |
| 283 | VideoStreamEncoderObserver::AdaptationSteps counts; |
| 284 | counts.num_framerate_reductions = fps_counters_[reason]; |
| 285 | counts.num_resolution_reductions = resolution_counters_[reason]; |
| 286 | return counts; |
| 287 | } |
| 288 | |
| 289 | void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter:: |
| 290 | IncrementFramerate(int reason) { |
| 291 | ++(fps_counters_[reason]); |
| 292 | } |
| 293 | |
| 294 | void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter:: |
| 295 | IncrementResolution(int reason) { |
| 296 | ++(resolution_counters_[reason]); |
| 297 | } |
| 298 | |
| 299 | void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter:: |
| 300 | DecrementFramerate(int reason) { |
| 301 | if (fps_counters_[reason] == 0) { |
| 302 | // Balanced mode: Adapt up is in a different order, switch reason. |
| 303 | // E.g. framerate adapt down: quality (2), framerate adapt up: cpu (3). |
| 304 | // 1. Down resolution (cpu): res={quality:0,cpu:1}, fps={quality:0,cpu:0} |
| 305 | // 2. Down fps (quality): res={quality:0,cpu:1}, fps={quality:1,cpu:0} |
| 306 | // 3. Up fps (cpu): res={quality:1,cpu:0}, fps={quality:0,cpu:0} |
| 307 | // 4. Up resolution (quality): res={quality:0,cpu:0}, fps={quality:0,cpu:0} |
| 308 | RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason."; |
| 309 | RTC_DCHECK_GT(FramerateCount(), 0) << "Framerate not downgraded."; |
| 310 | MoveCount(&resolution_counters_, reason); |
| 311 | MoveCount(&fps_counters_, (reason + 1) % kScaleReasonSize); |
| 312 | } |
| 313 | --(fps_counters_[reason]); |
| 314 | RTC_DCHECK_GE(fps_counters_[reason], 0); |
| 315 | } |
| 316 | |
| 317 | void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter:: |
| 318 | DecrementResolution(int reason) { |
| 319 | if (resolution_counters_[reason] == 0) { |
| 320 | // Balanced mode: Adapt up is in a different order, switch reason. |
| 321 | RTC_DCHECK_GT(TotalCount(reason), 0) << "No downgrade for reason."; |
| 322 | RTC_DCHECK_GT(ResolutionCount(), 0) << "Resolution not downgraded."; |
| 323 | MoveCount(&fps_counters_, reason); |
| 324 | MoveCount(&resolution_counters_, (reason + 1) % kScaleReasonSize); |
| 325 | } |
| 326 | --(resolution_counters_[reason]); |
| 327 | RTC_DCHECK_GE(resolution_counters_[reason], 0); |
| 328 | } |
| 329 | |
| 330 | void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter:: |
| 331 | DecrementFramerate(int reason, int cur_fps) { |
| 332 | DecrementFramerate(reason); |
| 333 | // Reset if at max fps (i.e. in case of fewer steps up than down). |
| 334 | if (cur_fps == std::numeric_limits<int>::max()) |
| 335 | absl::c_fill(fps_counters_, 0); |
| 336 | } |
| 337 | |
| 338 | int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount() |
| 339 | const { |
| 340 | return Count(fps_counters_); |
| 341 | } |
| 342 | |
| 343 | int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter:: |
| 344 | ResolutionCount() const { |
| 345 | return Count(resolution_counters_); |
| 346 | } |
| 347 | |
| 348 | int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::FramerateCount( |
| 349 | int reason) const { |
| 350 | return fps_counters_[reason]; |
| 351 | } |
| 352 | |
| 353 | int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ResolutionCount( |
| 354 | int reason) const { |
| 355 | return resolution_counters_[reason]; |
| 356 | } |
| 357 | |
| 358 | int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::TotalCount( |
| 359 | int reason) const { |
| 360 | return FramerateCount(reason) + ResolutionCount(reason); |
| 361 | } |
| 362 | |
| 363 | int OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::Count( |
| 364 | const std::vector<int>& counters) const { |
| 365 | return absl::c_accumulate(counters, 0); |
| 366 | } |
| 367 | |
| 368 | void OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::MoveCount( |
| 369 | std::vector<int>* counters, |
| 370 | int from_reason) { |
| 371 | int to_reason = (from_reason + 1) % kScaleReasonSize; |
| 372 | ++((*counters)[to_reason]); |
| 373 | --((*counters)[from_reason]); |
| 374 | } |
| 375 | |
| 376 | std::string |
| 377 | OveruseFrameDetectorResourceAdaptationModule::AdaptCounter::ToString( |
| 378 | const std::vector<int>& counters) const { |
| 379 | rtc::StringBuilder ss; |
| 380 | for (size_t reason = 0; reason < kScaleReasonSize; ++reason) { |
| 381 | ss << (reason ? " cpu" : "quality") << ":" << counters[reason]; |
| 382 | } |
| 383 | return ss.Release(); |
| 384 | } |
| 385 | |
| 386 | OveruseFrameDetectorResourceAdaptationModule:: |
| 387 | OveruseFrameDetectorResourceAdaptationModule( |
Henrik Boström | 382cc6d | 2020-01-07 10:15:04 +0100 | [diff] [blame] | 388 | VideoStreamEncoder* video_stream_encoder, |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 389 | VideoSourceSinkController* video_source_sink_controller, |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 390 | std::unique_ptr<OveruseFrameDetector> overuse_detector, |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 391 | VideoStreamEncoderObserver* encoder_stats_observer, |
| 392 | ResourceAdaptationModuleListener* adaptation_listener) |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 393 | : encoder_queue_(nullptr), |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 394 | adaptation_listener_(adaptation_listener), |
Henrik Boström | 382cc6d | 2020-01-07 10:15:04 +0100 | [diff] [blame] | 395 | video_stream_encoder_(video_stream_encoder), |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 396 | video_source_sink_controller_(video_source_sink_controller), |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 397 | degradation_preference_(DegradationPreference::DISABLED), |
| 398 | adapt_counters_(), |
| 399 | balanced_settings_(), |
| 400 | last_adaptation_request_(absl::nullopt), |
| 401 | last_frame_pixel_count_(absl::nullopt), |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 402 | source_restrictor_(std::make_unique<VideoSourceRestrictor>( |
| 403 | video_source_sink_controller)), |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 404 | overuse_detector_(std::move(overuse_detector)), |
| 405 | codec_max_framerate_(-1), |
| 406 | encoder_start_bitrate_bps_(0), |
| 407 | is_quality_scaler_enabled_(false), |
| 408 | encoder_config_(), |
| 409 | encoder_(nullptr), |
| 410 | encoder_stats_observer_(encoder_stats_observer) { |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 411 | RTC_DCHECK(adaptation_listener_); |
Henrik Boström | 382cc6d | 2020-01-07 10:15:04 +0100 | [diff] [blame] | 412 | RTC_DCHECK(video_stream_encoder_); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 413 | RTC_DCHECK(overuse_detector_); |
| 414 | RTC_DCHECK(encoder_stats_observer_); |
| 415 | } |
| 416 | |
| 417 | OveruseFrameDetectorResourceAdaptationModule:: |
| 418 | ~OveruseFrameDetectorResourceAdaptationModule() {} |
| 419 | |
| 420 | void OveruseFrameDetectorResourceAdaptationModule::Initialize( |
| 421 | rtc::TaskQueue* encoder_queue) { |
| 422 | RTC_DCHECK(!encoder_queue_); |
| 423 | encoder_queue_ = encoder_queue; |
| 424 | RTC_DCHECK(encoder_queue_); |
| 425 | } |
| 426 | |
| 427 | void OveruseFrameDetectorResourceAdaptationModule::SetEncoder( |
| 428 | VideoEncoder* encoder) { |
| 429 | RTC_DCHECK(encoder_queue_); |
| 430 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 431 | encoder_ = encoder; |
| 432 | } |
| 433 | |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 434 | void OveruseFrameDetectorResourceAdaptationModule::StartCheckForOveruse( |
| 435 | ResourceAdaptationModuleListener* adaptation_listener) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 436 | RTC_DCHECK(encoder_queue_); |
| 437 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 438 | RTC_DCHECK(encoder_); |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 439 | // TODO(hbos): When AdaptUp() and AdaptDown() are no longer invoked outside |
| 440 | // the interval between StartCheckForOveruse() and StopCheckForOveruse(), |
| 441 | // support configuring which |adaptation_listener_| to use on the fly. It is |
| 442 | // currently hardcoded for the entire lifetime of the module in order to |
| 443 | // support adaptation caused by VideoStreamEncoder or QualityScaler invoking |
| 444 | // AdaptUp() and AdaptDown() even when the OveruseDetector is inactive. |
| 445 | RTC_DCHECK_EQ(adaptation_listener, adaptation_listener_); |
Henrik Boström | 382cc6d | 2020-01-07 10:15:04 +0100 | [diff] [blame] | 446 | overuse_detector_->StartCheckForOveruse( |
| 447 | encoder_queue_, video_stream_encoder_->GetCpuOveruseOptions(), this); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 448 | } |
| 449 | |
| 450 | void OveruseFrameDetectorResourceAdaptationModule::StopCheckForOveruse() { |
| 451 | RTC_DCHECK(encoder_queue_); |
| 452 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 453 | overuse_detector_->StopCheckForOveruse(); |
| 454 | } |
| 455 | |
| 456 | void OveruseFrameDetectorResourceAdaptationModule::FrameCaptured( |
| 457 | const VideoFrame& frame, |
| 458 | int64_t time_when_first_seen_us) { |
| 459 | RTC_DCHECK(encoder_queue_); |
| 460 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 461 | overuse_detector_->FrameCaptured(frame, time_when_first_seen_us); |
| 462 | } |
| 463 | |
| 464 | void OveruseFrameDetectorResourceAdaptationModule::FrameSent( |
| 465 | uint32_t timestamp, |
| 466 | int64_t time_sent_in_us, |
| 467 | int64_t capture_time_us, |
| 468 | absl::optional<int> encode_duration_us) { |
| 469 | RTC_DCHECK(encoder_queue_); |
| 470 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 471 | overuse_detector_->FrameSent(timestamp, time_sent_in_us, capture_time_us, |
| 472 | encode_duration_us); |
| 473 | } |
| 474 | |
| 475 | void OveruseFrameDetectorResourceAdaptationModule::SetLastFramePixelCount( |
| 476 | absl::optional<int> last_frame_pixel_count) { |
| 477 | RTC_DCHECK(encoder_queue_); |
| 478 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 479 | last_frame_pixel_count_ = last_frame_pixel_count; |
| 480 | } |
| 481 | |
| 482 | void OveruseFrameDetectorResourceAdaptationModule::SetEncoderConfig( |
| 483 | VideoEncoderConfig encoder_config) { |
| 484 | RTC_DCHECK(encoder_queue_); |
| 485 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 486 | encoder_config_ = std::move(encoder_config); |
| 487 | } |
| 488 | |
| 489 | void OveruseFrameDetectorResourceAdaptationModule::SetCodecMaxFramerate( |
| 490 | int codec_max_framerate) { |
| 491 | RTC_DCHECK(encoder_queue_); |
| 492 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 493 | codec_max_framerate_ = codec_max_framerate; |
| 494 | } |
| 495 | |
| 496 | void OveruseFrameDetectorResourceAdaptationModule::SetEncoderStartBitrateBps( |
| 497 | uint32_t encoder_start_bitrate_bps) { |
| 498 | RTC_DCHECK(encoder_queue_); |
| 499 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 500 | encoder_start_bitrate_bps_ = encoder_start_bitrate_bps; |
| 501 | } |
| 502 | |
| 503 | void OveruseFrameDetectorResourceAdaptationModule::SetIsQualityScalerEnabled( |
| 504 | bool is_quality_scaler_enabled) { |
| 505 | RTC_DCHECK(encoder_queue_); |
| 506 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 507 | is_quality_scaler_enabled_ = is_quality_scaler_enabled; |
| 508 | } |
| 509 | |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 510 | void OveruseFrameDetectorResourceAdaptationModule:: |
| 511 | SetHasInputVideoAndDegradationPreference( |
| 512 | bool has_input_video, |
| 513 | DegradationPreference degradation_preference) { |
| 514 | source_restrictor_->SetHasInputVideoAndDegradationPreference( |
| 515 | has_input_video, degradation_preference); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 516 | encoder_queue_->PostTask([this, degradation_preference] { |
| 517 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 518 | if (degradation_preference_ != degradation_preference) { |
| 519 | // Reset adaptation state, so that we're not tricked into thinking there's |
| 520 | // an already pending request of the same type. |
| 521 | last_adaptation_request_.reset(); |
| 522 | if (degradation_preference == DegradationPreference::BALANCED || |
| 523 | degradation_preference_ == DegradationPreference::BALANCED) { |
| 524 | // TODO(asapersson): Consider removing |adapt_counters_| map and use one |
| 525 | // AdaptCounter for all modes. |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 526 | source_restrictor_->ResetPixelFpsCount(); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 527 | adapt_counters_.clear(); |
| 528 | } |
| 529 | } |
| 530 | degradation_preference_ = degradation_preference; |
| 531 | }); |
| 532 | } |
| 533 | |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 534 | void OveruseFrameDetectorResourceAdaptationModule::RefreshTargetFramerate() { |
| 535 | RTC_DCHECK(encoder_queue_); |
| 536 | RTC_DCHECK_RUN_ON(encoder_queue_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 537 | // We need the "sink wants" from the |video_source_sink_controller_| because |
| 538 | // the controller filters its current settings as "sink wants" differently |
| 539 | // depending degradation preferences. |
| 540 | // TODO(https://crbug.com/webrtc/11222): When degradation preference-related |
| 541 | // changes to settings are handled by this class instead, we can remove the |
| 542 | // dependency on the controller; the VideoSourceRestrictions outputted by this |
| 543 | // module will then be the "final" settings, including the max frame rate. |
| 544 | auto sink_wants = video_source_sink_controller_->CurrentSettingsToSinkWants(); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 545 | // Get the current target framerate, ie the maximum framerate as specified by |
| 546 | // the current codec configuration, or any limit imposed by cpu adaption in |
| 547 | // maintain-resolution or balanced mode. This is used to make sure overuse |
| 548 | // detection doesn't needlessly trigger in low and/or variable framerate |
| 549 | // scenarios. |
| 550 | int target_framerate = |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 551 | std::min(codec_max_framerate_, sink_wants.max_framerate_fps); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 552 | overuse_detector_->OnTargetFramerateUpdated(target_framerate); |
| 553 | } |
| 554 | |
| 555 | void OveruseFrameDetectorResourceAdaptationModule::ResetAdaptationCounters() { |
| 556 | RTC_DCHECK(encoder_queue_); |
| 557 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 558 | last_adaptation_request_.reset(); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 559 | source_restrictor_->ResetPixelFpsCount(); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 560 | adapt_counters_.clear(); |
| 561 | } |
| 562 | |
| 563 | void OveruseFrameDetectorResourceAdaptationModule::AdaptUp(AdaptReason reason) { |
| 564 | RTC_DCHECK(encoder_queue_); |
| 565 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 566 | const AdaptCounter& adapt_counter = GetConstAdaptCounter(); |
| 567 | int num_downgrades = adapt_counter.TotalCount(reason); |
| 568 | if (num_downgrades == 0) |
| 569 | return; |
| 570 | RTC_DCHECK_GT(num_downgrades, 0); |
| 571 | |
| 572 | AdaptationRequest adaptation_request = { |
| 573 | *last_frame_pixel_count_, encoder_stats_observer_->GetInputFrameRate(), |
| 574 | AdaptationRequest::Mode::kAdaptUp}; |
| 575 | |
| 576 | bool adapt_up_requested = |
| 577 | last_adaptation_request_ && |
| 578 | last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptUp; |
| 579 | |
| 580 | if (EffectiveDegradataionPreference() == |
| 581 | DegradationPreference::MAINTAIN_FRAMERATE) { |
| 582 | if (adapt_up_requested && |
| 583 | adaptation_request.input_pixel_count_ <= |
| 584 | last_adaptation_request_->input_pixel_count_) { |
| 585 | // Don't request higher resolution if the current resolution is not |
| 586 | // higher than the last time we asked for the resolution to be higher. |
| 587 | return; |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | switch (EffectiveDegradataionPreference()) { |
| 592 | case DegradationPreference::BALANCED: { |
| 593 | // Check if quality should be increased based on bitrate. |
| 594 | if (reason == kQuality && |
Åsa Persson | f5e71e4 | 2020-01-09 10:04:53 +0100 | [diff] [blame] | 595 | !balanced_settings_.CanAdaptUp(encoder_config_.codec_type, |
| 596 | *last_frame_pixel_count_, |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 597 | encoder_start_bitrate_bps_)) { |
| 598 | return; |
| 599 | } |
| 600 | // Try scale up framerate, if higher. |
| 601 | int fps = balanced_settings_.MaxFps(encoder_config_.codec_type, |
| 602 | *last_frame_pixel_count_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 603 | if (source_restrictor_->IncreaseFramerate(fps)) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 604 | GetAdaptCounter().DecrementFramerate(reason, fps); |
| 605 | // Reset framerate in case of fewer fps steps down than up. |
| 606 | if (adapt_counter.FramerateCount() == 0 && |
| 607 | fps != std::numeric_limits<int>::max()) { |
| 608 | RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting."; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 609 | source_restrictor_->IncreaseFramerate( |
| 610 | std::numeric_limits<int>::max()); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 611 | } |
| 612 | break; |
| 613 | } |
| 614 | // Check if resolution should be increased based on bitrate. |
| 615 | if (reason == kQuality && |
| 616 | !balanced_settings_.CanAdaptUpResolution( |
Åsa Persson | f5e71e4 | 2020-01-09 10:04:53 +0100 | [diff] [blame] | 617 | encoder_config_.codec_type, *last_frame_pixel_count_, |
| 618 | encoder_start_bitrate_bps_)) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 619 | return; |
| 620 | } |
| 621 | // Scale up resolution. |
| 622 | RTC_FALLTHROUGH(); |
| 623 | } |
| 624 | case DegradationPreference::MAINTAIN_FRAMERATE: { |
| 625 | // Check if resolution should be increased based on bitrate and |
| 626 | // limits specified by encoder capabilities. |
| 627 | if (reason == kQuality && |
| 628 | !CanAdaptUpResolution(*last_frame_pixel_count_, |
| 629 | encoder_start_bitrate_bps_)) { |
| 630 | return; |
| 631 | } |
| 632 | |
| 633 | // Scale up resolution. |
| 634 | int pixel_count = adaptation_request.input_pixel_count_; |
| 635 | if (adapt_counter.ResolutionCount() == 1) { |
| 636 | RTC_LOG(LS_INFO) << "Removing resolution down-scaling setting."; |
| 637 | pixel_count = std::numeric_limits<int>::max(); |
| 638 | } |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 639 | if (!source_restrictor_->RequestHigherResolutionThan(pixel_count)) |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 640 | return; |
| 641 | GetAdaptCounter().DecrementResolution(reason); |
| 642 | break; |
| 643 | } |
| 644 | case DegradationPreference::MAINTAIN_RESOLUTION: { |
| 645 | // Scale up framerate. |
| 646 | int fps = adaptation_request.framerate_fps_; |
| 647 | if (adapt_counter.FramerateCount() == 1) { |
| 648 | RTC_LOG(LS_INFO) << "Removing framerate down-scaling setting."; |
| 649 | fps = std::numeric_limits<int>::max(); |
| 650 | } |
| 651 | |
| 652 | const int requested_framerate = |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 653 | source_restrictor_->RequestHigherFramerateThan(fps); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 654 | if (requested_framerate == -1) { |
| 655 | overuse_detector_->OnTargetFramerateUpdated(codec_max_framerate_); |
| 656 | return; |
| 657 | } |
| 658 | overuse_detector_->OnTargetFramerateUpdated( |
| 659 | std::min(codec_max_framerate_, requested_framerate)); |
| 660 | GetAdaptCounter().DecrementFramerate(reason); |
| 661 | break; |
| 662 | } |
| 663 | case DegradationPreference::DISABLED: |
| 664 | return; |
| 665 | } |
| 666 | |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 667 | // Tell the adaptation listener to reconfigure the source for us according to |
| 668 | // the latest adaptation. |
| 669 | adaptation_listener_->OnVideoSourceRestrictionsUpdated( |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 670 | source_restrictor_->source_restrictions()); |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 671 | |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 672 | last_adaptation_request_.emplace(adaptation_request); |
| 673 | |
| 674 | UpdateAdaptationStats(reason); |
| 675 | |
| 676 | RTC_LOG(LS_INFO) << adapt_counter.ToString(); |
| 677 | } |
| 678 | |
| 679 | bool OveruseFrameDetectorResourceAdaptationModule::AdaptDown( |
| 680 | AdaptReason reason) { |
| 681 | RTC_DCHECK(encoder_queue_); |
| 682 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 683 | AdaptationRequest adaptation_request = { |
| 684 | *last_frame_pixel_count_, encoder_stats_observer_->GetInputFrameRate(), |
| 685 | AdaptationRequest::Mode::kAdaptDown}; |
| 686 | |
| 687 | bool downgrade_requested = |
| 688 | last_adaptation_request_ && |
| 689 | last_adaptation_request_->mode_ == AdaptationRequest::Mode::kAdaptDown; |
| 690 | |
| 691 | bool did_adapt = true; |
| 692 | |
| 693 | switch (EffectiveDegradataionPreference()) { |
| 694 | case DegradationPreference::BALANCED: |
| 695 | break; |
| 696 | case DegradationPreference::MAINTAIN_FRAMERATE: |
| 697 | if (downgrade_requested && |
| 698 | adaptation_request.input_pixel_count_ >= |
| 699 | last_adaptation_request_->input_pixel_count_) { |
| 700 | // Don't request lower resolution if the current resolution is not |
| 701 | // lower than the last time we asked for the resolution to be lowered. |
| 702 | return true; |
| 703 | } |
| 704 | break; |
| 705 | case DegradationPreference::MAINTAIN_RESOLUTION: |
| 706 | if (adaptation_request.framerate_fps_ <= 0 || |
| 707 | (downgrade_requested && |
| 708 | adaptation_request.framerate_fps_ < kMinFramerateFps)) { |
| 709 | // If no input fps estimate available, can't determine how to scale down |
| 710 | // framerate. Otherwise, don't request lower framerate if we don't have |
| 711 | // a valid frame rate. Since framerate, unlike resolution, is a measure |
| 712 | // we have to estimate, and can fluctuate naturally over time, don't |
| 713 | // make the same kind of limitations as for resolution, but trust the |
| 714 | // overuse detector to not trigger too often. |
| 715 | return true; |
| 716 | } |
| 717 | break; |
| 718 | case DegradationPreference::DISABLED: |
| 719 | return true; |
| 720 | } |
| 721 | |
| 722 | switch (EffectiveDegradataionPreference()) { |
| 723 | case DegradationPreference::BALANCED: { |
| 724 | // Try scale down framerate, if lower. |
| 725 | int fps = balanced_settings_.MinFps(encoder_config_.codec_type, |
| 726 | *last_frame_pixel_count_); |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 727 | if (source_restrictor_->RestrictFramerate(fps)) { |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 728 | GetAdaptCounter().IncrementFramerate(reason); |
| 729 | // Check if requested fps is higher (or close to) input fps. |
| 730 | absl::optional<int> min_diff = |
| 731 | balanced_settings_.MinFpsDiff(*last_frame_pixel_count_); |
| 732 | if (min_diff && adaptation_request.framerate_fps_ > 0) { |
| 733 | int fps_diff = adaptation_request.framerate_fps_ - fps; |
| 734 | if (fps_diff < min_diff.value()) { |
| 735 | did_adapt = false; |
| 736 | } |
| 737 | } |
| 738 | break; |
| 739 | } |
| 740 | // Scale down resolution. |
| 741 | RTC_FALLTHROUGH(); |
| 742 | } |
| 743 | case DegradationPreference::MAINTAIN_FRAMERATE: { |
| 744 | // Scale down resolution. |
| 745 | bool min_pixels_reached = false; |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 746 | if (!source_restrictor_->RequestResolutionLowerThan( |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 747 | adaptation_request.input_pixel_count_, |
| 748 | encoder_->GetEncoderInfo().scaling_settings.min_pixels_per_frame, |
| 749 | &min_pixels_reached)) { |
| 750 | if (min_pixels_reached) |
| 751 | encoder_stats_observer_->OnMinPixelLimitReached(); |
| 752 | return true; |
| 753 | } |
| 754 | GetAdaptCounter().IncrementResolution(reason); |
| 755 | break; |
| 756 | } |
| 757 | case DegradationPreference::MAINTAIN_RESOLUTION: { |
| 758 | // Scale down framerate. |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 759 | const int requested_framerate = |
| 760 | source_restrictor_->RequestFramerateLowerThan( |
| 761 | adaptation_request.framerate_fps_); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 762 | if (requested_framerate == -1) |
| 763 | return true; |
| 764 | RTC_DCHECK_NE(codec_max_framerate_, -1); |
| 765 | overuse_detector_->OnTargetFramerateUpdated( |
| 766 | std::min(codec_max_framerate_, requested_framerate)); |
| 767 | GetAdaptCounter().IncrementFramerate(reason); |
| 768 | break; |
| 769 | } |
| 770 | case DegradationPreference::DISABLED: |
| 771 | RTC_NOTREACHED(); |
| 772 | } |
| 773 | |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 774 | // Tell the adaptation listener to reconfigure the source for us according to |
| 775 | // the latest adaptation. |
| 776 | adaptation_listener_->OnVideoSourceRestrictionsUpdated( |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 777 | source_restrictor_->source_restrictions()); |
Henrik Boström | d238200 | 2020-01-10 15:44:01 +0100 | [diff] [blame] | 778 | |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 779 | last_adaptation_request_.emplace(adaptation_request); |
| 780 | |
| 781 | UpdateAdaptationStats(reason); |
| 782 | |
| 783 | RTC_LOG(LS_INFO) << GetConstAdaptCounter().ToString(); |
| 784 | return did_adapt; |
| 785 | } |
| 786 | |
| 787 | // TODO(nisse): Delete, once AdaptReason and AdaptationReason are merged. |
| 788 | void OveruseFrameDetectorResourceAdaptationModule::UpdateAdaptationStats( |
| 789 | AdaptReason reason) { |
| 790 | switch (reason) { |
| 791 | case kCpu: |
| 792 | encoder_stats_observer_->OnAdaptationChanged( |
| 793 | VideoStreamEncoderObserver::AdaptationReason::kCpu, |
| 794 | GetActiveCounts(kCpu), GetActiveCounts(kQuality)); |
| 795 | break; |
| 796 | case kQuality: |
| 797 | encoder_stats_observer_->OnAdaptationChanged( |
| 798 | VideoStreamEncoderObserver::AdaptationReason::kQuality, |
| 799 | GetActiveCounts(kCpu), GetActiveCounts(kQuality)); |
| 800 | break; |
| 801 | } |
| 802 | } |
| 803 | |
| 804 | VideoStreamEncoderObserver::AdaptationSteps |
| 805 | OveruseFrameDetectorResourceAdaptationModule::GetActiveCounts( |
| 806 | AdaptReason reason) { |
| 807 | RTC_DCHECK(encoder_queue_); |
| 808 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 809 | VideoStreamEncoderObserver::AdaptationSteps counts = |
| 810 | GetConstAdaptCounter().Counts(reason); |
| 811 | switch (reason) { |
| 812 | case kCpu: |
| 813 | if (!IsFramerateScalingEnabled(degradation_preference_)) |
| 814 | counts.num_framerate_reductions = absl::nullopt; |
| 815 | if (!IsResolutionScalingEnabled(degradation_preference_)) |
| 816 | counts.num_resolution_reductions = absl::nullopt; |
| 817 | break; |
| 818 | case kQuality: |
| 819 | if (!IsFramerateScalingEnabled(degradation_preference_) || |
| 820 | !is_quality_scaler_enabled_) { |
| 821 | counts.num_framerate_reductions = absl::nullopt; |
| 822 | } |
| 823 | if (!IsResolutionScalingEnabled(degradation_preference_) || |
| 824 | !is_quality_scaler_enabled_) { |
| 825 | counts.num_resolution_reductions = absl::nullopt; |
| 826 | } |
| 827 | break; |
| 828 | } |
| 829 | return counts; |
| 830 | } |
| 831 | |
| 832 | DegradationPreference OveruseFrameDetectorResourceAdaptationModule:: |
| 833 | EffectiveDegradataionPreference() { |
| 834 | // Balanced mode for screenshare works via automatic animation detection: |
| 835 | // Resolution is capped for fullscreen animated content. |
| 836 | // Adapatation is done only via framerate downgrade. |
| 837 | // Thus effective degradation preference is MAINTAIN_RESOLUTION. |
| 838 | return (encoder_config_.content_type == |
| 839 | VideoEncoderConfig::ContentType::kScreen && |
| 840 | degradation_preference_ == DegradationPreference::BALANCED) |
| 841 | ? DegradationPreference::MAINTAIN_RESOLUTION |
| 842 | : degradation_preference_; |
| 843 | } |
| 844 | |
| 845 | OveruseFrameDetectorResourceAdaptationModule::AdaptCounter& |
| 846 | OveruseFrameDetectorResourceAdaptationModule::GetAdaptCounter() { |
| 847 | return adapt_counters_[degradation_preference_]; |
| 848 | } |
| 849 | |
| 850 | const OveruseFrameDetectorResourceAdaptationModule::AdaptCounter& |
| 851 | OveruseFrameDetectorResourceAdaptationModule::GetConstAdaptCounter() { |
| 852 | RTC_DCHECK(encoder_queue_); |
| 853 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 854 | return adapt_counters_[degradation_preference_]; |
| 855 | } |
| 856 | |
| 857 | absl::optional<VideoEncoder::QpThresholds> |
| 858 | OveruseFrameDetectorResourceAdaptationModule::GetQpThresholds() const { |
| 859 | RTC_DCHECK(encoder_queue_); |
| 860 | RTC_DCHECK_RUN_ON(encoder_queue_); |
| 861 | RTC_DCHECK(last_frame_pixel_count_.has_value()); |
| 862 | return balanced_settings_.GetQpThresholds(encoder_config_.codec_type, |
| 863 | last_frame_pixel_count_.value()); |
| 864 | } |
| 865 | |
| 866 | bool OveruseFrameDetectorResourceAdaptationModule::CanAdaptUpResolution( |
| 867 | int pixels, |
| 868 | uint32_t bitrate_bps) const { |
| 869 | absl::optional<VideoEncoder::ResolutionBitrateLimits> bitrate_limits = |
Henrik Boström | ce0ea49 | 2020-01-13 11:27:18 +0100 | [diff] [blame] | 870 | GetEncoderBitrateLimits( |
| 871 | encoder_->GetEncoderInfo(), |
| 872 | source_restrictor_->GetHigherResolutionThan(pixels)); |
Henrik Boström | b08882b | 2020-01-07 10:11:17 +0100 | [diff] [blame] | 873 | if (!bitrate_limits.has_value() || bitrate_bps == 0) { |
| 874 | return true; // No limit configured or bitrate provided. |
| 875 | } |
| 876 | RTC_DCHECK_GE(bitrate_limits->frame_size_pixels, pixels); |
| 877 | return bitrate_bps >= |
| 878 | static_cast<uint32_t>(bitrate_limits->min_start_bitrate_bps); |
| 879 | } |
| 880 | |
| 881 | } // namespace webrtc |