1 // libjingle 2 // Copyright 2010 Google Inc. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // 7 // 1. Redistributions of source code must retain the above copyright notice, 8 // this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright notice, 10 // this list of conditions and the following disclaimer in the documentation 11 // and/or other materials provided with the distribution. 12 // 3. The name of the author may not be used to endorse or promote products 13 // derived from this software without specific prior written permission. 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 #include "talk/media/base/videoadapter.h" 27 28 #include <limits.h> // For INT_MAX 29 30 #include "talk/base/logging.h" 31 #include "talk/base/timeutils.h" 32 #include "talk/media/base/constants.h" 33 #include "talk/media/base/videoframe.h" 34 35 namespace cricket { 36 37 // TODO(fbarchard): Make downgrades settable 38 static const int kMaxCpuDowngrades = 2; // Downgrade at most 2 times for CPU. 39 // The number of milliseconds of data to require before acting on cpu sampling 40 // information. 41 static const size_t kCpuLoadMinSampleTime = 5000; 42 // The amount of weight to give to each new cpu load sample. The lower the 43 // value, the slower we'll adapt to changing cpu conditions. 44 static const float kCpuLoadWeightCoefficient = 0.4f; 45 // The seed value for the cpu load moving average. 46 static const float kCpuLoadInitialAverage = 0.5f; 47 48 // TODO(fbarchard): Consider making scale factor table settable, to allow 49 // application to select quality vs performance tradeoff. 50 // TODO(fbarchard): Add framerate scaling to tables for 1/2 framerate. 51 // List of scale factors that adapter will scale by. 52 #if defined(IOS) || defined(ANDROID) 53 // Mobile needs 1/4 scale for VGA (640 x 360) to QQVGA (160 x 90) 54 // or 1/4 scale for HVGA (480 x 270) to QQHVGA (120 x 67) 55 static const int kMinNumPixels = 120 * 67; 56 static float kScaleFactors[] = { 57 1.f / 1.f, // Full size. 58 3.f / 4.f, // 3/4 scale. 59 1.f / 2.f, // 1/2 scale. 60 3.f / 8.f, // 3/8 scale. 61 1.f / 4.f, // 1/4 scale. 62 }; 63 #else 64 // Desktop needs 1/8 scale for HD (1280 x 720) to QQVGA (160 x 90) 65 static const int kMinNumPixels = 160 * 100; 66 static float kScaleFactors[] = { 67 1.f / 1.f, // Full size. 68 3.f / 4.f, // 3/4 scale. 69 1.f / 2.f, // 1/2 scale. 70 3.f / 8.f, // 3/8 scale. 71 1.f / 4.f, // 1/4 scale. 72 3.f / 16.f, // 3/16 scale. 73 1.f / 8.f // 1/8 scale. 74 }; 75 #endif 76 77 static const int kNumScaleFactors = ARRAY_SIZE(kScaleFactors); 78 79 // Find the scale factor that, when applied to width and height, is closest 80 // to num_pixels. 81 float VideoAdapter::FindClosestScale(int width, int height, 82 int target_num_pixels) { 83 if (!target_num_pixels) { 84 return 0.f; 85 } 86 int best_distance = INT_MAX; 87 int best_index = kNumScaleFactors - 1; // Default to max scale. 88 for (int i = 0; i < kNumScaleFactors; ++i) { 89 int test_num_pixels = static_cast<int>(width * kScaleFactors[i] * 90 height * kScaleFactors[i]); 91 int diff = test_num_pixels - target_num_pixels; 92 if (diff < 0) { 93 diff = -diff; 94 } 95 if (diff < best_distance) { 96 best_distance = diff; 97 best_index = i; 98 if (best_distance == 0) { // Found exact match. 99 break; 100 } 101 } 102 } 103 return kScaleFactors[best_index]; 104 } 105 106 // Finds the scale factor that, when applied to width and height, produces 107 // fewer than num_pixels. 108 float VideoAdapter::FindLowerScale(int width, int height, 109 int target_num_pixels) { 110 if (!target_num_pixels) { 111 return 0.f; 112 } 113 int best_distance = INT_MAX; 114 int best_index = kNumScaleFactors - 1; // Default to max scale. 115 for (int i = 0; i < kNumScaleFactors; ++i) { 116 int test_num_pixels = static_cast<int>(width * kScaleFactors[i] * 117 height * kScaleFactors[i]); 118 int diff = target_num_pixels - test_num_pixels; 119 if (diff >= 0 && diff < best_distance) { 120 best_distance = diff; 121 best_index = i; 122 if (best_distance == 0) { // Found exact match. 123 break; 124 } 125 } 126 } 127 return kScaleFactors[best_index]; 128 } 129 130 // There are several frame sizes used by Adapter. This explains them 131 // input_format - set once by server to frame size expected from the camera. 132 // output_format - size that output would like to be. Includes framerate. 133 // output_num_pixels - size that output should be constrained to. Used to 134 // compute output_format from in_frame. 135 // in_frame - actual camera captured frame size, which is typically the same 136 // as input_format. This can also be rotated or cropped for aspect ratio. 137 // out_frame - actual frame output by adapter. Should be a direct scale of 138 // in_frame maintaining rotation and aspect ratio. 139 // OnOutputFormatRequest - server requests you send this resolution based on 140 // view requests. 141 // OnEncoderResolutionRequest - encoder requests you send this resolution based 142 // on bandwidth 143 // OnCpuLoadUpdated - cpu monitor requests you send this resolution based on 144 // cpu load. 145 146 /////////////////////////////////////////////////////////////////////// 147 // Implementation of VideoAdapter 148 VideoAdapter::VideoAdapter() 149 : output_num_pixels_(INT_MAX), 150 black_output_(false), 151 is_black_(false), 152 interval_next_frame_(0) { 153 } 154 155 VideoAdapter::~VideoAdapter() { 156 } 157 158 void VideoAdapter::SetInputFormat(const VideoFrame& in_frame) { 159 talk_base::CritScope cs(&critical_section_); 160 input_format_.width = static_cast<int>(in_frame.GetWidth()); 161 input_format_.height = static_cast<int>(in_frame.GetHeight()); 162 } 163 164 void VideoAdapter::SetInputFormat(const VideoFormat& format) { 165 talk_base::CritScope cs(&critical_section_); 166 input_format_ = format; 167 output_format_.interval = talk_base::_max( 168 output_format_.interval, input_format_.interval); 169 } 170 171 void VideoAdapter::SetOutputFormat(const VideoFormat& format) { 172 talk_base::CritScope cs(&critical_section_); 173 output_format_ = format; 174 output_num_pixels_ = output_format_.width * output_format_.height; 175 output_format_.interval = talk_base::_max( 176 output_format_.interval, input_format_.interval); 177 } 178 179 const VideoFormat& VideoAdapter::input_format() { 180 talk_base::CritScope cs(&critical_section_); 181 return input_format_; 182 } 183 184 const VideoFormat& VideoAdapter::output_format() { 185 talk_base::CritScope cs(&critical_section_); 186 return output_format_; 187 } 188 189 void VideoAdapter::SetBlackOutput(bool black) { 190 talk_base::CritScope cs(&critical_section_); 191 black_output_ = black; 192 } 193 194 // Constrain output resolution to this many pixels overall 195 void VideoAdapter::SetOutputNumPixels(int num_pixels) { 196 output_num_pixels_ = num_pixels; 197 } 198 199 int VideoAdapter::GetOutputNumPixels() const { 200 return output_num_pixels_; 201 } 202 203 // TODO(fbarchard): Add AdaptFrameRate function that only drops frames but 204 // not resolution. 205 bool VideoAdapter::AdaptFrame(const VideoFrame* in_frame, 206 const VideoFrame** out_frame) { 207 talk_base::CritScope cs(&critical_section_); 208 if (!in_frame || !out_frame) { 209 return false; 210 } 211 212 // Update input to actual frame dimensions. 213 SetInputFormat(*in_frame); 214 215 // Drop the input frame if necessary. 216 bool should_drop = false; 217 if (!output_num_pixels_) { 218 // Drop all frames as the output format is 0x0. 219 should_drop = true; 220 } else { 221 // Drop some frames based on input fps and output fps. 222 // Normally output fps is less than input fps. 223 // TODO(fbarchard): Consider adjusting interval to reflect the adjusted 224 // interval between frames after dropping some frames. 225 interval_next_frame_ += input_format_.interval; 226 if (output_format_.interval > 0) { 227 if (interval_next_frame_ >= output_format_.interval) { 228 interval_next_frame_ %= output_format_.interval; 229 } else { 230 should_drop = true; 231 } 232 } 233 } 234 if (should_drop) { 235 *out_frame = NULL; 236 return true; 237 } 238 239 if (output_num_pixels_) { 240 float scale = VideoAdapter::FindClosestScale( 241 static_cast<int>(in_frame->GetWidth()), 242 static_cast<int>(in_frame->GetHeight()), 243 output_num_pixels_); 244 output_format_.width = static_cast<int>(in_frame->GetWidth() * scale + .5f); 245 output_format_.height = static_cast<int>(in_frame->GetHeight() * scale + 246 .5f); 247 } 248 249 if (!StretchToOutputFrame(in_frame)) { 250 return false; 251 } 252 253 *out_frame = output_frame_.get(); 254 return true; 255 } 256 257 bool VideoAdapter::StretchToOutputFrame(const VideoFrame* in_frame) { 258 int output_width = output_format_.width; 259 int output_height = output_format_.height; 260 261 // Create and stretch the output frame if it has not been created yet or its 262 // size is not same as the expected. 263 bool stretched = false; 264 if (!output_frame_ || 265 output_frame_->GetWidth() != static_cast<size_t>(output_width) || 266 output_frame_->GetHeight() != static_cast<size_t>(output_height)) { 267 output_frame_.reset( 268 in_frame->Stretch(output_width, output_height, true, true)); 269 if (!output_frame_) { 270 LOG(LS_WARNING) << "Adapter failed to stretch frame to " 271 << output_width << "x" << output_height; 272 return false; 273 } 274 stretched = true; 275 is_black_ = false; 276 } 277 278 if (!black_output_) { 279 if (!stretched) { 280 // The output frame does not need to be blacken and has not been stretched 281 // from the input frame yet, stretch the input frame. This is the most 282 // common case. 283 in_frame->StretchToFrame(output_frame_.get(), true, true); 284 } 285 is_black_ = false; 286 } else { 287 if (!is_black_) { 288 output_frame_->SetToBlack(); 289 is_black_ = true; 290 } 291 output_frame_->SetElapsedTime(in_frame->GetElapsedTime()); 292 output_frame_->SetTimeStamp(in_frame->GetTimeStamp()); 293 } 294 295 return true; 296 } 297 298 /////////////////////////////////////////////////////////////////////// 299 // Implementation of CoordinatedVideoAdapter 300 CoordinatedVideoAdapter::CoordinatedVideoAdapter() 301 : cpu_adaptation_(false), 302 cpu_smoothing_(false), 303 gd_adaptation_(true), 304 view_adaptation_(true), 305 view_switch_(false), 306 cpu_downgrade_count_(0), 307 cpu_adapt_wait_time_(0), 308 high_system_threshold_(kHighSystemCpuThreshold), 309 low_system_threshold_(kLowSystemCpuThreshold), 310 process_threshold_(kProcessCpuThreshold), 311 view_desired_num_pixels_(INT_MAX), 312 view_desired_interval_(0), 313 encoder_desired_num_pixels_(INT_MAX), 314 cpu_desired_num_pixels_(INT_MAX), 315 adapt_reason_(0), 316 system_load_average_(kCpuLoadInitialAverage) { 317 } 318 319 // Helper function to UPGRADE or DOWNGRADE a number of pixels 320 void CoordinatedVideoAdapter::StepPixelCount( 321 CoordinatedVideoAdapter::AdaptRequest request, 322 int* num_pixels) { 323 switch (request) { 324 case CoordinatedVideoAdapter::DOWNGRADE: 325 *num_pixels /= 2; 326 break; 327 328 case CoordinatedVideoAdapter::UPGRADE: 329 *num_pixels *= 2; 330 break; 331 332 default: // No change in pixel count 333 break; 334 } 335 return; 336 } 337 338 // Find the adaptation request of the cpu based on the load. Return UPGRADE if 339 // the load is low, DOWNGRADE if the load is high, and KEEP otherwise. 340 CoordinatedVideoAdapter::AdaptRequest CoordinatedVideoAdapter::FindCpuRequest( 341 int current_cpus, int max_cpus, 342 float process_load, float system_load) { 343 // Downgrade if system is high and plugin is at least more than midrange. 344 if (system_load >= high_system_threshold_ * max_cpus && 345 process_load >= process_threshold_ * current_cpus) { 346 return CoordinatedVideoAdapter::DOWNGRADE; 347 // Upgrade if system is low. 348 } else if (system_load < low_system_threshold_ * max_cpus) { 349 return CoordinatedVideoAdapter::UPGRADE; 350 } 351 return CoordinatedVideoAdapter::KEEP; 352 } 353 354 // A remote view request for a new resolution. 355 void CoordinatedVideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { 356 talk_base::CritScope cs(&request_critical_section_); 357 if (!view_adaptation_) { 358 return; 359 } 360 // Set output for initial aspect ratio in mediachannel unittests. 361 int old_num_pixels = GetOutputNumPixels(); 362 SetOutputFormat(format); 363 SetOutputNumPixels(old_num_pixels); 364 view_desired_num_pixels_ = format.width * format.height; 365 view_desired_interval_ = format.interval; 366 int new_width, new_height; 367 bool changed = AdaptToMinimumFormat(&new_width, &new_height); 368 LOG(LS_INFO) << "VAdapt View Request: " 369 << format.width << "x" << format.height 370 << " Pixels: " << view_desired_num_pixels_ 371 << " Changed: " << (changed ? "true" : "false") 372 << " To: " << new_width << "x" << new_height; 373 } 374 375 // A Bandwidth GD request for new resolution 376 void CoordinatedVideoAdapter::OnEncoderResolutionRequest( 377 int width, int height, AdaptRequest request) { 378 talk_base::CritScope cs(&request_critical_section_); 379 if (!gd_adaptation_) { 380 return; 381 } 382 int old_encoder_desired_num_pixels = encoder_desired_num_pixels_; 383 if (KEEP != request) { 384 int new_encoder_desired_num_pixels = width * height; 385 int old_num_pixels = GetOutputNumPixels(); 386 if (new_encoder_desired_num_pixels != old_num_pixels) { 387 LOG(LS_VERBOSE) << "VAdapt GD resolution stale. Ignored"; 388 } else { 389 // Update the encoder desired format based on the request. 390 encoder_desired_num_pixels_ = new_encoder_desired_num_pixels; 391 StepPixelCount(request, &encoder_desired_num_pixels_); 392 } 393 } 394 int new_width, new_height; 395 bool changed = AdaptToMinimumFormat(&new_width, &new_height); 396 397 // Ignore up or keep if no change. 398 if (DOWNGRADE != request && view_switch_ && !changed) { 399 encoder_desired_num_pixels_ = old_encoder_desired_num_pixels; 400 LOG(LS_VERBOSE) << "VAdapt ignoring GD request."; 401 } 402 403 LOG(LS_INFO) << "VAdapt GD Request: " 404 << (DOWNGRADE == request ? "down" : 405 (UPGRADE == request ? "up" : "keep")) 406 << " From: " << width << "x" << height 407 << " Pixels: " << encoder_desired_num_pixels_ 408 << " Changed: " << (changed ? "true" : "false") 409 << " To: " << new_width << "x" << new_height; 410 } 411 412 // A CPU request for new resolution 413 void CoordinatedVideoAdapter::OnCpuLoadUpdated( 414 int current_cpus, int max_cpus, float process_load, float system_load) { 415 talk_base::CritScope cs(&request_critical_section_); 416 if (!cpu_adaptation_) { 417 return; 418 } 419 // Update the moving average of system load. Even if we aren't smoothing, 420 // we'll still calculate this information, in case smoothing is later enabled. 421 system_load_average_ = kCpuLoadWeightCoefficient * system_load + 422 (1.0f - kCpuLoadWeightCoefficient) * system_load_average_; 423 if (cpu_smoothing_) { 424 system_load = system_load_average_; 425 } 426 // If we haven't started taking samples yet, wait until we have at least 427 // the correct number of samples per the wait time. 428 if (cpu_adapt_wait_time_ == 0) { 429 cpu_adapt_wait_time_ = talk_base::TimeAfter(kCpuLoadMinSampleTime); 430 } 431 AdaptRequest request = FindCpuRequest(current_cpus, max_cpus, 432 process_load, system_load); 433 // Make sure we're not adapting too quickly. 434 if (request != KEEP) { 435 if (talk_base::TimeIsLater(talk_base::Time(), 436 cpu_adapt_wait_time_)) { 437 LOG(LS_VERBOSE) << "VAdapt CPU load high/low but do not adapt until " 438 << talk_base::TimeUntil(cpu_adapt_wait_time_) << " ms"; 439 request = KEEP; 440 } 441 } 442 443 // Update how many times we have downgraded due to the cpu load. 444 switch (request) { 445 case DOWNGRADE: 446 // Ignore downgrades if we have downgraded the maximum times. 447 if (cpu_downgrade_count_ < kMaxCpuDowngrades) { 448 ++cpu_downgrade_count_; 449 } else { 450 LOG(LS_VERBOSE) << "VAdapt CPU load high but do not downgrade " 451 "because maximum downgrades reached"; 452 SignalCpuAdaptationUnable(); 453 } 454 break; 455 case UPGRADE: 456 if (cpu_downgrade_count_ > 0) { 457 bool is_min = IsMinimumFormat(cpu_desired_num_pixels_); 458 if (is_min) { 459 --cpu_downgrade_count_; 460 } else { 461 LOG(LS_VERBOSE) << "VAdapt CPU load low but do not upgrade " 462 "because cpu is not limiting resolution"; 463 } 464 } else { 465 LOG(LS_VERBOSE) << "VAdapt CPU load low but do not upgrade " 466 "because minimum downgrades reached"; 467 } 468 break; 469 case KEEP: 470 default: 471 break; 472 } 473 if (KEEP != request) { 474 // TODO(fbarchard): compute stepping up/down from OutputNumPixels but 475 // clamp to inputpixels / 4 (2 steps) 476 cpu_desired_num_pixels_ = cpu_downgrade_count_ == 0 ? INT_MAX : 477 static_cast<int>(input_format().width * input_format().height >> 478 cpu_downgrade_count_); 479 } 480 int new_width, new_height; 481 bool changed = AdaptToMinimumFormat(&new_width, &new_height); 482 LOG(LS_INFO) << "VAdapt CPU Request: " 483 << (DOWNGRADE == request ? "down" : 484 (UPGRADE == request ? "up" : "keep")) 485 << " Process: " << process_load 486 << " System: " << system_load 487 << " Steps: " << cpu_downgrade_count_ 488 << " Changed: " << (changed ? "true" : "false") 489 << " To: " << new_width << "x" << new_height; 490 } 491 492 // Called by cpu adapter on up requests. 493 bool CoordinatedVideoAdapter::IsMinimumFormat(int pixels) { 494 // Find closest scale factor that matches input resolution to min_num_pixels 495 // and set that for output resolution. This is not needed for VideoAdapter, 496 // but provides feedback to unittests and users on expected resolution. 497 // Actual resolution is based on input frame. 498 VideoFormat new_output = output_format(); 499 VideoFormat input = input_format(); 500 if (input_format().IsSize0x0()) { 501 input = new_output; 502 } 503 float scale = 1.0f; 504 if (!input.IsSize0x0()) { 505 scale = FindClosestScale(input.width, 506 input.height, 507 pixels); 508 } 509 new_output.width = static_cast<int>(input.width * scale + .5f); 510 new_output.height = static_cast<int>(input.height * scale + .5f); 511 int new_pixels = new_output.width * new_output.height; 512 int num_pixels = GetOutputNumPixels(); 513 return new_pixels <= num_pixels; 514 } 515 516 // Called by all coordinators when there is a change. 517 bool CoordinatedVideoAdapter::AdaptToMinimumFormat(int* new_width, 518 int* new_height) { 519 VideoFormat new_output = output_format(); 520 VideoFormat input = input_format(); 521 if (input_format().IsSize0x0()) { 522 input = new_output; 523 } 524 int old_num_pixels = GetOutputNumPixels(); 525 // Find resolution that respects ViewRequest or less pixels. 526 int view_desired_num_pixels = view_desired_num_pixels_; 527 int min_num_pixels = view_desired_num_pixels_; 528 if (!input.IsSize0x0()) { 529 float scale = FindLowerScale(input.width, input.height, min_num_pixels); 530 min_num_pixels = view_desired_num_pixels = 531 static_cast<int>(input.width * input.height * scale * scale + .5f); 532 } 533 // Reduce resolution further, if necessary, based on encoder bandwidth (GD). 534 if (encoder_desired_num_pixels_ && 535 (encoder_desired_num_pixels_ < min_num_pixels)) { 536 min_num_pixels = encoder_desired_num_pixels_; 537 } 538 // Reduce resolution further, if necessary, based on CPU. 539 if (cpu_adaptation_ && cpu_desired_num_pixels_ && 540 (cpu_desired_num_pixels_ < min_num_pixels)) { 541 min_num_pixels = cpu_desired_num_pixels_; 542 } 543 544 // Determine which factors are keeping adapter resolution low. 545 // Caveat: Does not consider framerate. 546 adapt_reason_ = static_cast<AdaptReason>(0); 547 if (view_desired_num_pixels == min_num_pixels) { 548 adapt_reason_ |= ADAPTREASON_VIEW; 549 } 550 if (encoder_desired_num_pixels_ == min_num_pixels) { 551 adapt_reason_ |= ADAPTREASON_BANDWIDTH; 552 } 553 if (cpu_desired_num_pixels_ == min_num_pixels) { 554 adapt_reason_ |= ADAPTREASON_CPU; 555 } 556 557 // Prevent going below QQVGA. 558 if (min_num_pixels > 0 && min_num_pixels < kMinNumPixels) { 559 min_num_pixels = kMinNumPixels; 560 } 561 SetOutputNumPixels(min_num_pixels); 562 563 // Find closest scale factor that matches input resolution to min_num_pixels 564 // and set that for output resolution. This is not needed for VideoAdapter, 565 // but provides feedback to unittests and users on expected resolution. 566 // Actual resolution is based on input frame. 567 float scale = 1.0f; 568 if (!input.IsSize0x0()) { 569 scale = FindClosestScale(input.width, input.height, min_num_pixels); 570 } 571 if (scale == 1.0f) { 572 adapt_reason_ = 0; 573 } 574 *new_width = new_output.width = static_cast<int>(input.width * scale + .5f); 575 *new_height = new_output.height = static_cast<int>(input.height * scale + 576 .5f); 577 new_output.interval = view_desired_interval_; 578 SetOutputFormat(new_output); 579 int new_num_pixels = GetOutputNumPixels(); 580 bool changed = new_num_pixels != old_num_pixels; 581 582 static const char* kReasons[8] = { 583 "None", 584 "CPU", 585 "BANDWIDTH", 586 "CPU+BANDWIDTH", 587 "VIEW", 588 "CPU+VIEW", 589 "BANDWIDTH+VIEW", 590 "CPU+BANDWIDTH+VIEW", 591 }; 592 593 LOG(LS_VERBOSE) << "VAdapt Status View: " << view_desired_num_pixels_ 594 << " GD: " << encoder_desired_num_pixels_ 595 << " CPU: " << cpu_desired_num_pixels_ 596 << " Pixels: " << min_num_pixels 597 << " Input: " << input.width 598 << "x" << input.height 599 << " Scale: " << scale 600 << " Resolution: " << new_output.width 601 << "x" << new_output.height 602 << " Changed: " << (changed ? "true" : "false") 603 << " Reason: " << kReasons[adapt_reason_]; 604 605 if (changed) { 606 // When any adaptation occurs, historic CPU load levels are no longer 607 // accurate. Clear out our state so we can re-learn at the new normal. 608 cpu_adapt_wait_time_ = talk_base::TimeAfter(kCpuLoadMinSampleTime); 609 system_load_average_ = kCpuLoadInitialAverage; 610 } 611 612 return changed; 613 } 614 615 } // namespace cricket 616