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