OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2010 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/media/base/videoadapter.h" | 11 #include "webrtc/media/base/videoadapter.h" |
12 | 12 |
13 #include <limits.h> // For INT_MAX | 13 #include <limits.h> // For INT_MAX |
14 #include <algorithm> | 14 #include <algorithm> |
15 | 15 |
16 #include "webrtc/base/logging.h" | 16 #include "webrtc/base/logging.h" |
17 #include "webrtc/base/timeutils.h" | 17 #include "webrtc/base/timeutils.h" |
18 #include "webrtc/media/base/constants.h" | 18 #include "webrtc/media/base/constants.h" |
19 #include "webrtc/media/base/videocommon.h" | 19 #include "webrtc/media/base/videocommon.h" |
20 #include "webrtc/media/base/videoframe.h" | 20 #include "webrtc/media/base/videoframe.h" |
21 | 21 |
22 namespace cricket { | 22 namespace cricket { |
23 | 23 |
24 // TODO(fbarchard): Make downgrades settable | |
25 static const int kMaxCpuDowngrades = 2; // Downgrade at most 2 times for CPU. | |
26 // The number of cpu samples to require before adapting. This value depends on | |
27 // the cpu monitor sampling frequency being 2000ms. | |
28 static const int kCpuLoadMinSamples = 3; | |
29 // The amount of weight to give to each new cpu load sample. The lower the | |
30 // value, the slower we'll adapt to changing cpu conditions. | |
31 static const float kCpuLoadWeightCoefficient = 0.4f; | |
32 // The seed value for the cpu load moving average. | |
33 static const float kCpuLoadInitialAverage = 0.5f; | |
34 | |
35 // Desktop needs 1/8 scale for HD (1280 x 720) to QQVGA (160 x 90) | 24 // Desktop needs 1/8 scale for HD (1280 x 720) to QQVGA (160 x 90) |
36 static const float kScaleFactors[] = { | 25 static const float kScaleFactors[] = { |
37 1.f / 1.f, // Full size. | 26 1.f / 1.f, // Full size. |
38 3.f / 4.f, // 3/4 scale. | 27 3.f / 4.f, // 3/4 scale. |
39 1.f / 2.f, // 1/2 scale. | 28 1.f / 2.f, // 1/2 scale. |
40 3.f / 8.f, // 3/8 scale. | 29 3.f / 8.f, // 3/8 scale. |
41 1.f / 4.f, // 1/4 scale. | 30 1.f / 4.f, // 1/4 scale. |
42 3.f / 16.f, // 3/16 scale. | 31 3.f / 16.f, // 3/16 scale. |
43 1.f / 8.f, // 1/8 scale. | 32 1.f / 8.f, // 1/8 scale. |
44 0.f // End of table. | 33 0.f // End of table. |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
191 LOG(LS_INFO) << "VAdapt Input Resolution Change: " | 180 LOG(LS_INFO) << "VAdapt Input Resolution Change: " |
192 << "Previous input resolution: " | 181 << "Previous input resolution: " |
193 << previous_width << "x" << previous_height | 182 << previous_width << "x" << previous_height |
194 << " New input resolution: " | 183 << " New input resolution: " |
195 << format.width << "x" << format.height | 184 << format.width << "x" << format.height |
196 << " New output resolution: " | 185 << " New output resolution: " |
197 << width << "x" << height; | 186 << width << "x" << height; |
198 } | 187 } |
199 } | 188 } |
200 | 189 |
201 void CoordinatedVideoAdapter::set_cpu_smoothing(bool enable) { | |
202 LOG(LS_INFO) << "CPU smoothing is now " | |
203 << (enable ? "enabled" : "disabled"); | |
204 cpu_smoothing_ = enable; | |
205 } | |
206 | |
207 void VideoAdapter::SetOutputFormat(const VideoFormat& format) { | 190 void VideoAdapter::SetOutputFormat(const VideoFormat& format) { |
208 rtc::CritScope cs(&critical_section_); | 191 rtc::CritScope cs(&critical_section_); |
209 int64_t old_output_interval = output_format_.interval; | 192 int64_t old_output_interval = output_format_.interval; |
210 output_format_ = format; | 193 output_format_ = format; |
211 output_num_pixels_ = output_format_.width * output_format_.height; | 194 output_num_pixels_ = output_format_.width * output_format_.height; |
212 output_format_.interval = | 195 output_format_.interval = |
213 std::max(output_format_.interval, input_format_.interval); | 196 std::max(output_format_.interval, input_format_.interval); |
214 if (old_output_interval != output_format_.interval) { | 197 if (old_output_interval != output_format_.interval) { |
215 LOG(LS_INFO) << "VAdapt output interval changed from " | 198 LOG(LS_INFO) << "VAdapt output interval changed from " |
216 << old_output_interval << " to " << output_format_.interval; | 199 << old_output_interval << " to " << output_format_.interval; |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 } | 322 } |
340 | 323 |
341 /////////////////////////////////////////////////////////////////////// | 324 /////////////////////////////////////////////////////////////////////// |
342 // Implementation of CoordinatedVideoAdapter | 325 // Implementation of CoordinatedVideoAdapter |
343 CoordinatedVideoAdapter::CoordinatedVideoAdapter() | 326 CoordinatedVideoAdapter::CoordinatedVideoAdapter() |
344 : cpu_adaptation_(true), | 327 : cpu_adaptation_(true), |
345 cpu_smoothing_(false), | 328 cpu_smoothing_(false), |
346 gd_adaptation_(true), | 329 gd_adaptation_(true), |
347 view_adaptation_(true), | 330 view_adaptation_(true), |
348 view_switch_(false), | 331 view_switch_(false), |
349 cpu_downgrade_count_(0), | |
350 cpu_load_min_samples_(kCpuLoadMinSamples), | |
351 cpu_load_num_samples_(0), | |
352 high_system_threshold_(kHighSystemCpuThreshold), | |
353 low_system_threshold_(kLowSystemCpuThreshold), | |
354 process_threshold_(kProcessCpuThreshold), | |
355 view_desired_num_pixels_(INT_MAX), | 332 view_desired_num_pixels_(INT_MAX), |
356 view_desired_interval_(0), | 333 view_desired_interval_(0), |
357 encoder_desired_num_pixels_(INT_MAX), | 334 encoder_desired_num_pixels_(INT_MAX), |
358 cpu_desired_num_pixels_(INT_MAX), | 335 cpu_desired_num_pixels_(INT_MAX), |
359 adapt_reason_(ADAPTREASON_NONE), | 336 adapt_reason_(ADAPTREASON_NONE) {} |
360 system_load_average_(kCpuLoadInitialAverage) { | |
361 } | |
362 | 337 |
363 // Helper function to UPGRADE or DOWNGRADE a number of pixels | 338 // Helper function to UPGRADE or DOWNGRADE a number of pixels |
364 void CoordinatedVideoAdapter::StepPixelCount( | 339 void CoordinatedVideoAdapter::StepPixelCount( |
365 CoordinatedVideoAdapter::AdaptRequest request, | 340 CoordinatedVideoAdapter::AdaptRequest request, |
366 int* num_pixels) { | 341 int* num_pixels) { |
367 switch (request) { | 342 switch (request) { |
368 case CoordinatedVideoAdapter::DOWNGRADE: | 343 case CoordinatedVideoAdapter::DOWNGRADE: |
369 *num_pixels /= 2; | 344 *num_pixels /= 2; |
370 break; | 345 break; |
371 | 346 |
372 case CoordinatedVideoAdapter::UPGRADE: | 347 case CoordinatedVideoAdapter::UPGRADE: |
373 *num_pixels *= 2; | 348 *num_pixels *= 2; |
374 break; | 349 break; |
375 | 350 |
376 default: // No change in pixel count | 351 default: // No change in pixel count |
377 break; | 352 break; |
378 } | 353 } |
379 return; | 354 return; |
380 } | 355 } |
381 | 356 |
382 // Find the adaptation request of the cpu based on the load. Return UPGRADE if | |
383 // the load is low, DOWNGRADE if the load is high, and KEEP otherwise. | |
384 CoordinatedVideoAdapter::AdaptRequest CoordinatedVideoAdapter::FindCpuRequest( | |
385 int current_cpus, int max_cpus, | |
386 float process_load, float system_load) { | |
387 // Downgrade if system is high and plugin is at least more than midrange. | |
388 if (system_load >= high_system_threshold_ * max_cpus && | |
389 process_load >= process_threshold_ * current_cpus) { | |
390 return CoordinatedVideoAdapter::DOWNGRADE; | |
391 // Upgrade if system is low. | |
392 } else if (system_load < low_system_threshold_ * max_cpus) { | |
393 return CoordinatedVideoAdapter::UPGRADE; | |
394 } | |
395 return CoordinatedVideoAdapter::KEEP; | |
396 } | |
397 | 357 |
398 // A remote view request for a new resolution. | 358 // A remote view request for a new resolution. |
399 void CoordinatedVideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { | 359 void CoordinatedVideoAdapter::OnOutputFormatRequest(const VideoFormat& format) { |
400 rtc::CritScope cs(&request_critical_section_); | 360 rtc::CritScope cs(&request_critical_section_); |
401 if (!view_adaptation_) { | 361 if (!view_adaptation_) { |
402 return; | 362 return; |
403 } | 363 } |
404 // Set output for initial aspect ratio in mediachannel unittests. | 364 // Set output for initial aspect ratio in mediachannel unittests. |
405 int old_num_pixels = GetOutputNumPixels(); | 365 int old_num_pixels = GetOutputNumPixels(); |
406 SetOutputFormat(format); | 366 SetOutputFormat(format); |
407 SetOutputNumPixels(old_num_pixels); | 367 SetOutputNumPixels(old_num_pixels); |
408 view_desired_num_pixels_ = format.width * format.height; | 368 view_desired_num_pixels_ = format.width * format.height; |
409 view_desired_interval_ = format.interval; | 369 view_desired_interval_ = format.interval; |
410 int new_width, new_height; | 370 int new_width, new_height; |
411 bool changed = AdaptToMinimumFormat(&new_width, &new_height); | 371 bool changed = AdaptToMinimumFormat(&new_width, &new_height); |
412 LOG(LS_INFO) << "VAdapt View Request: " | 372 LOG(LS_INFO) << "VAdapt View Request: " |
413 << format.width << "x" << format.height | 373 << format.width << "x" << format.height |
414 << " Pixels: " << view_desired_num_pixels_ | 374 << " Pixels: " << view_desired_num_pixels_ |
415 << " Changed: " << (changed ? "true" : "false") | 375 << " Changed: " << (changed ? "true" : "false") |
416 << " To: " << new_width << "x" << new_height; | 376 << " To: " << new_width << "x" << new_height; |
417 } | 377 } |
418 | 378 |
419 void CoordinatedVideoAdapter::set_cpu_load_min_samples( | |
420 int cpu_load_min_samples) { | |
421 if (cpu_load_min_samples_ != cpu_load_min_samples) { | |
422 LOG(LS_INFO) << "VAdapt Change Cpu Adapt Min Samples from: " | |
423 << cpu_load_min_samples_ << " to " | |
424 << cpu_load_min_samples; | |
425 cpu_load_min_samples_ = cpu_load_min_samples; | |
426 } | |
427 } | |
428 | |
429 void CoordinatedVideoAdapter::set_high_system_threshold( | |
430 float high_system_threshold) { | |
431 ASSERT(high_system_threshold <= 1.0f); | |
432 ASSERT(high_system_threshold >= 0.0f); | |
433 if (high_system_threshold_ != high_system_threshold) { | |
434 LOG(LS_INFO) << "VAdapt Change High System Threshold from: " | |
435 << high_system_threshold_ << " to " << high_system_threshold; | |
436 high_system_threshold_ = high_system_threshold; | |
437 } | |
438 } | |
439 | |
440 void CoordinatedVideoAdapter::set_low_system_threshold( | |
441 float low_system_threshold) { | |
442 ASSERT(low_system_threshold <= 1.0f); | |
443 ASSERT(low_system_threshold >= 0.0f); | |
444 if (low_system_threshold_ != low_system_threshold) { | |
445 LOG(LS_INFO) << "VAdapt Change Low System Threshold from: " | |
446 << low_system_threshold_ << " to " << low_system_threshold; | |
447 low_system_threshold_ = low_system_threshold; | |
448 } | |
449 } | |
450 | |
451 void CoordinatedVideoAdapter::set_process_threshold(float process_threshold) { | |
452 ASSERT(process_threshold <= 1.0f); | |
453 ASSERT(process_threshold >= 0.0f); | |
454 if (process_threshold_ != process_threshold) { | |
455 LOG(LS_INFO) << "VAdapt Change High Process Threshold from: " | |
456 << process_threshold_ << " to " << process_threshold; | |
457 process_threshold_ = process_threshold; | |
458 } | |
459 } | |
460 | |
461 // A Bandwidth GD request for new resolution | 379 // A Bandwidth GD request for new resolution |
462 void CoordinatedVideoAdapter::OnEncoderResolutionRequest( | 380 void CoordinatedVideoAdapter::OnEncoderResolutionRequest( |
463 int width, int height, AdaptRequest request) { | 381 int width, int height, AdaptRequest request) { |
464 rtc::CritScope cs(&request_critical_section_); | 382 rtc::CritScope cs(&request_critical_section_); |
465 if (!gd_adaptation_) { | 383 if (!gd_adaptation_) { |
466 return; | 384 return; |
467 } | 385 } |
468 int old_encoder_desired_num_pixels = encoder_desired_num_pixels_; | 386 int old_encoder_desired_num_pixels = encoder_desired_num_pixels_; |
469 if (KEEP != request) { | 387 if (KEEP != request) { |
470 int new_encoder_desired_num_pixels = width * height; | 388 int new_encoder_desired_num_pixels = width * height; |
(...skipping 17 matching lines...) Expand all Loading... |
488 | 406 |
489 LOG(LS_INFO) << "VAdapt GD Request: " | 407 LOG(LS_INFO) << "VAdapt GD Request: " |
490 << (DOWNGRADE == request ? "down" : | 408 << (DOWNGRADE == request ? "down" : |
491 (UPGRADE == request ? "up" : "keep")) | 409 (UPGRADE == request ? "up" : "keep")) |
492 << " From: " << width << "x" << height | 410 << " From: " << width << "x" << height |
493 << " Pixels: " << encoder_desired_num_pixels_ | 411 << " Pixels: " << encoder_desired_num_pixels_ |
494 << " Changed: " << (changed ? "true" : "false") | 412 << " Changed: " << (changed ? "true" : "false") |
495 << " To: " << new_width << "x" << new_height; | 413 << " To: " << new_width << "x" << new_height; |
496 } | 414 } |
497 | 415 |
498 // A Bandwidth GD request for new resolution | 416 void CoordinatedVideoAdapter::OnLimitResolution(int max_number_of_pixels) { |
499 void CoordinatedVideoAdapter::OnCpuResolutionRequest(AdaptRequest request) { | |
500 rtc::CritScope cs(&request_critical_section_); | 417 rtc::CritScope cs(&request_critical_section_); |
501 if (!cpu_adaptation_) { | 418 if (!cpu_adaptation_) { |
502 return; | 419 return; |
503 } | 420 } |
504 // Update how many times we have downgraded due to the cpu load. | 421 |
505 switch (request) { | 422 const VideoFormat& input = input_format(); |
506 case DOWNGRADE: | 423 if (input.IsSize0x0()) { |
507 // Ignore downgrades if we have downgraded the maximum times. | 424 // TODO NOW.... Make sure AdaptToMinimumFormat can be called before first |
508 if (cpu_downgrade_count_ < kMaxCpuDowngrades) { | 425 // frame.... |
509 ++cpu_downgrade_count_; | 426 return; |
510 } else { | |
511 LOG(LS_VERBOSE) << "VAdapt CPU load high but do not downgrade " | |
512 "because maximum downgrades reached"; | |
513 SignalCpuAdaptationUnable(); | |
514 } | |
515 break; | |
516 case UPGRADE: | |
517 if (cpu_downgrade_count_ > 0) { | |
518 bool is_min = IsMinimumFormat(cpu_desired_num_pixels_); | |
519 if (is_min) { | |
520 --cpu_downgrade_count_; | |
521 } else { | |
522 LOG(LS_VERBOSE) << "VAdapt CPU load low but do not upgrade " | |
523 "because cpu is not limiting resolution"; | |
524 } | |
525 } else { | |
526 LOG(LS_VERBOSE) << "VAdapt CPU load low but do not upgrade " | |
527 "because minimum downgrades reached"; | |
528 } | |
529 break; | |
530 case KEEP: | |
531 default: | |
532 break; | |
533 } | 427 } |
534 if (KEEP != request) { | 428 cpu_desired_num_pixels_ = |
535 // TODO(fbarchard): compute stepping up/down from OutputNumPixels but | 429 std::min(input.width * input.height, max_number_of_pixels); |
536 // clamp to inputpixels / 4 (2 steps) | 430 |
537 cpu_desired_num_pixels_ = cpu_downgrade_count_ == 0 ? INT_MAX : | |
538 static_cast<int>(input_format().width * input_format().height >> | |
539 cpu_downgrade_count_); | |
540 } | |
541 int new_width, new_height; | 431 int new_width, new_height; |
542 bool changed = AdaptToMinimumFormat(&new_width, &new_height); | 432 bool changed = AdaptToMinimumFormat(&new_width, &new_height); |
543 LOG(LS_INFO) << "VAdapt CPU Request: " | 433 LOG(LS_INFO) << "VAdapt OnLimitResolution: " |
544 << (DOWNGRADE == request ? "down" : | |
545 (UPGRADE == request ? "up" : "keep")) | |
546 << " Steps: " << cpu_downgrade_count_ | |
547 << " Changed: " << (changed ? "true" : "false") | 434 << " Changed: " << (changed ? "true" : "false") |
548 << " To: " << new_width << "x" << new_height; | 435 << " To: " << new_width << "x" << new_height; |
549 } | 436 } |
550 | 437 |
551 // A CPU request for new resolution | |
552 // TODO(fbarchard): Move outside adapter. | |
553 void CoordinatedVideoAdapter::OnCpuLoadUpdated( | |
554 int current_cpus, int max_cpus, float process_load, float system_load) { | |
555 rtc::CritScope cs(&request_critical_section_); | |
556 if (!cpu_adaptation_) { | |
557 return; | |
558 } | |
559 // Update the moving average of system load. Even if we aren't smoothing, | |
560 // we'll still calculate this information, in case smoothing is later enabled. | |
561 system_load_average_ = kCpuLoadWeightCoefficient * system_load + | |
562 (1.0f - kCpuLoadWeightCoefficient) * system_load_average_; | |
563 ++cpu_load_num_samples_; | |
564 if (cpu_smoothing_) { | |
565 system_load = system_load_average_; | |
566 } | |
567 AdaptRequest request = FindCpuRequest(current_cpus, max_cpus, | |
568 process_load, system_load); | |
569 // Make sure we're not adapting too quickly. | |
570 if (request != KEEP) { | |
571 if (cpu_load_num_samples_ < cpu_load_min_samples_) { | |
572 LOG(LS_VERBOSE) << "VAdapt CPU load high/low but do not adapt until " | |
573 << (cpu_load_min_samples_ - cpu_load_num_samples_) | |
574 << " more samples"; | |
575 request = KEEP; | |
576 } | |
577 } | |
578 | |
579 OnCpuResolutionRequest(request); | |
580 } | |
581 | |
582 // Called by cpu adapter on up requests. | 438 // Called by cpu adapter on up requests. |
583 bool CoordinatedVideoAdapter::IsMinimumFormat(int pixels) { | 439 bool CoordinatedVideoAdapter::IsMinimumFormat(int pixels) { |
584 // Find closest scale factor that matches input resolution to min_num_pixels | 440 // Find closest scale factor that matches input resolution to min_num_pixels |
585 // and set that for output resolution. This is not needed for VideoAdapter, | 441 // and set that for output resolution. This is not needed for VideoAdapter, |
586 // but provides feedback to unittests and users on expected resolution. | 442 // but provides feedback to unittests and users on expected resolution. |
587 // Actual resolution is based on input frame. | 443 // Actual resolution is based on input frame. |
588 VideoFormat new_output = output_format(); | 444 VideoFormat new_output = output_format(); |
589 VideoFormat input = input_format(); | 445 VideoFormat input = input_format(); |
590 if (input_format().IsSize0x0()) { | 446 if (input_format().IsSize0x0()) { |
591 input = new_output; | 447 input = new_output; |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
682 << " CPU: " << cpu_desired_num_pixels_ | 538 << " CPU: " << cpu_desired_num_pixels_ |
683 << " Pixels: " << min_num_pixels | 539 << " Pixels: " << min_num_pixels |
684 << " Input: " << input.width | 540 << " Input: " << input.width |
685 << "x" << input.height | 541 << "x" << input.height |
686 << " Scale: " << scale | 542 << " Scale: " << scale |
687 << " Resolution: " << new_output.width | 543 << " Resolution: " << new_output.width |
688 << "x" << new_output.height | 544 << "x" << new_output.height |
689 << " Changed: " << (changed ? "true" : "false") | 545 << " Changed: " << (changed ? "true" : "false") |
690 << " Reason: " << kReasons[adapt_reason_]; | 546 << " Reason: " << kReasons[adapt_reason_]; |
691 | 547 |
692 if (changed) { | |
693 // When any adaptation occurs, historic CPU load levels are no longer | |
694 // accurate. Clear out our state so we can re-learn at the new normal. | |
695 cpu_load_num_samples_ = 0; | |
696 system_load_average_ = kCpuLoadInitialAverage; | |
697 } | |
698 | |
699 return changed; | 548 return changed; |
700 } | 549 } |
701 | 550 |
702 } // namespace cricket | 551 } // namespace cricket |
OLD | NEW |