OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 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 "video/send_statistics_proxy.h" | 11 #include "video/send_statistics_proxy.h" |
12 | 12 |
13 #include <algorithm> | 13 #include <algorithm> |
14 #include <cmath> | 14 #include <cmath> |
15 #include <map> | 15 #include <map> |
16 #include <vector> | 16 #include <vector> |
17 | 17 |
18 #include "common_types.h" | 18 #include "common_types.h" // NOLINT(build/include) |
19 #include "modules/video_coding/include/video_codec_interface.h" | 19 #include "modules/video_coding/include/video_codec_interface.h" |
20 #include "rtc_base/checks.h" | 20 #include "rtc_base/checks.h" |
21 #include "rtc_base/logging.h" | 21 #include "rtc_base/logging.h" |
| 22 #include "system_wrappers/include/field_trial.h" |
22 #include "system_wrappers/include/metrics.h" | 23 #include "system_wrappers/include/metrics.h" |
23 | 24 |
24 namespace webrtc { | 25 namespace webrtc { |
25 namespace { | 26 namespace { |
26 const float kEncodeTimeWeigthFactor = 0.5f; | 27 const float kEncodeTimeWeigthFactor = 0.5f; |
27 | 28 |
| 29 const char kVp8ForcedFallbackEncoderFieldTrial[] = |
| 30 "WebRTC-VP8-Forced-Fallback-Encoder"; |
| 31 const char kVp8SwCodecName[] = "libvpx"; |
| 32 |
28 // Used by histograms. Values of entries should not be changed. | 33 // Used by histograms. Values of entries should not be changed. |
29 enum HistogramCodecType { | 34 enum HistogramCodecType { |
30 kVideoUnknown = 0, | 35 kVideoUnknown = 0, |
31 kVideoVp8 = 1, | 36 kVideoVp8 = 1, |
32 kVideoVp9 = 2, | 37 kVideoVp9 = 2, |
33 kVideoH264 = 3, | 38 kVideoH264 = 3, |
34 kVideoMax = 64, | 39 kVideoMax = 64, |
35 }; | 40 }; |
36 | 41 |
37 const char* kRealtimePrefix = "WebRTC.Video."; | 42 const char* kRealtimePrefix = "WebRTC.Video."; |
(...skipping 23 matching lines...) Expand all Loading... |
61 default: | 66 default: |
62 return kVideoUnknown; | 67 return kVideoUnknown; |
63 } | 68 } |
64 } | 69 } |
65 | 70 |
66 void UpdateCodecTypeHistogram(const std::string& payload_name) { | 71 void UpdateCodecTypeHistogram(const std::string& payload_name) { |
67 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType", | 72 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.Encoder.CodecType", |
68 PayloadNameToHistogramCodecType(payload_name), | 73 PayloadNameToHistogramCodecType(payload_name), |
69 kVideoMax); | 74 kVideoMax); |
70 } | 75 } |
| 76 |
| 77 bool IsForcedFallbackPossible(const CodecSpecificInfo* codec_info) { |
| 78 return codec_info->codecType == kVideoCodecVP8 && |
| 79 codec_info->codecSpecific.VP8.simulcastIdx == 0 && |
| 80 (codec_info->codecSpecific.VP8.temporalIdx == 0 || |
| 81 codec_info->codecSpecific.VP8.temporalIdx == kNoTemporalIdx); |
| 82 } |
| 83 |
| 84 rtc::Optional<int> GetFallbackIntervalFromFieldTrial() { |
| 85 if (!webrtc::field_trial::IsEnabled(kVp8ForcedFallbackEncoderFieldTrial)) |
| 86 return rtc::Optional<int>(); |
| 87 |
| 88 std::string group = |
| 89 webrtc::field_trial::FindFullName(kVp8ForcedFallbackEncoderFieldTrial); |
| 90 if (group.empty()) |
| 91 return rtc::Optional<int>(); |
| 92 |
| 93 int low_kbps; |
| 94 int high_kbps; |
| 95 int min_low_ms; |
| 96 int min_pixels; |
| 97 if (sscanf(group.c_str(), "Enabled-%d,%d,%d,%d", &low_kbps, &high_kbps, |
| 98 &min_low_ms, &min_pixels) != 4) { |
| 99 return rtc::Optional<int>(); |
| 100 } |
| 101 |
| 102 if (min_low_ms <= 0 || min_pixels <= 0 || low_kbps <= 0 || |
| 103 high_kbps <= low_kbps) { |
| 104 return rtc::Optional<int>(); |
| 105 } |
| 106 return rtc::Optional<int>(min_low_ms); |
| 107 } |
71 } // namespace | 108 } // namespace |
72 | 109 |
73 | 110 |
74 const int SendStatisticsProxy::kStatsTimeoutMs = 5000; | 111 const int SendStatisticsProxy::kStatsTimeoutMs = 5000; |
75 | 112 |
76 SendStatisticsProxy::SendStatisticsProxy( | 113 SendStatisticsProxy::SendStatisticsProxy( |
77 Clock* clock, | 114 Clock* clock, |
78 const VideoSendStream::Config& config, | 115 const VideoSendStream::Config& config, |
79 VideoEncoderConfig::ContentType content_type) | 116 VideoEncoderConfig::ContentType content_type) |
80 : clock_(clock), | 117 : clock_(clock), |
81 payload_name_(config.encoder_settings.payload_name), | 118 payload_name_(config.encoder_settings.payload_name), |
82 rtp_config_(config.rtp), | 119 rtp_config_(config.rtp), |
| 120 min_first_fallback_interval_ms_(GetFallbackIntervalFromFieldTrial()), |
83 content_type_(content_type), | 121 content_type_(content_type), |
84 start_ms_(clock->TimeInMilliseconds()), | 122 start_ms_(clock->TimeInMilliseconds()), |
85 last_sent_frame_timestamp_(0), | 123 last_sent_frame_timestamp_(0), |
86 encode_time_(kEncodeTimeWeigthFactor), | 124 encode_time_(kEncodeTimeWeigthFactor), |
87 quality_downscales_(-1), | 125 quality_downscales_(-1), |
88 cpu_downscales_(-1), | 126 cpu_downscales_(-1), |
89 uma_container_( | 127 uma_container_( |
90 new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) { | 128 new UmaSamplesContainer(GetUmaPrefix(content_type_), stats_, clock)) { |
91 } | 129 } |
92 | 130 |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
403 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000); | 441 paused_time_counter_.Percent(metrics::kMinRunTimeInSeconds * 1000); |
404 if (paused_time_percent != -1) { | 442 if (paused_time_percent != -1) { |
405 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent", | 443 RTC_HISTOGRAMS_PERCENTAGE(kIndex, uma_prefix_ + "PausedTimeInPercent", |
406 paused_time_percent); | 444 paused_time_percent); |
407 LOG(LS_INFO) << uma_prefix_ << "PausedTimeInPercent " | 445 LOG(LS_INFO) << uma_prefix_ << "PausedTimeInPercent " |
408 << paused_time_percent; | 446 << paused_time_percent; |
409 } | 447 } |
410 } | 448 } |
411 } | 449 } |
412 | 450 |
| 451 if (fallback_info_.is_possible) { |
| 452 // Double interval since there is some time before fallback may occur. |
| 453 const int kMinRunTimeMs = 2 * metrics::kMinRunTimeInSeconds * 1000; |
| 454 int64_t elapsed_ms = fallback_info_.elapsed_ms; |
| 455 int fallback_time_percent = fallback_active_counter_.Percent(kMinRunTimeMs); |
| 456 if (fallback_time_percent != -1 && elapsed_ms >= kMinRunTimeMs) { |
| 457 RTC_HISTOGRAMS_PERCENTAGE( |
| 458 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackTimeInPercent.Vp8", |
| 459 fallback_time_percent); |
| 460 RTC_HISTOGRAMS_COUNTS_100( |
| 461 kIndex, uma_prefix_ + "Encoder.ForcedSwFallbackChangesPerMinute.Vp8", |
| 462 fallback_info_.on_off_events * 60 / (elapsed_ms / 1000)); |
| 463 } |
| 464 } |
| 465 |
413 AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats(); | 466 AggregatedStats total_bytes_per_sec = total_byte_counter_.GetStats(); |
414 if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { | 467 if (total_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { |
415 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps", | 468 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "BitrateSentInKbps", |
416 total_bytes_per_sec.average * 8 / 1000); | 469 total_bytes_per_sec.average * 8 / 1000); |
417 LOG(LS_INFO) << uma_prefix_ << "BitrateSentInBps, " | 470 LOG(LS_INFO) << uma_prefix_ << "BitrateSentInBps, " |
418 << total_bytes_per_sec.ToStringWithMultiplier(8); | 471 << total_bytes_per_sec.ToStringWithMultiplier(8); |
419 } | 472 } |
420 AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats(); | 473 AggregatedStats media_bytes_per_sec = media_byte_counter_.GetStats(); |
421 if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { | 474 if (media_bytes_per_sec.num_samples > kMinRequiredPeriodicSamples) { |
422 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps", | 475 RTC_HISTOGRAMS_COUNTS_10000(kIndex, uma_prefix_ + "MediaBitrateSentInKbps", |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
613 | 666 |
614 // Check if video is paused/resumed. | 667 // Check if video is paused/resumed. |
615 uma_container_->target_rate_updates_.last_paused_or_resumed = | 668 uma_container_->target_rate_updates_.last_paused_or_resumed = |
616 (bitrate_bps == 0) != was_paused; | 669 (bitrate_bps == 0) != was_paused; |
617 } | 670 } |
618 uma_container_->target_rate_updates_.last_ms = now; | 671 uma_container_->target_rate_updates_.last_ms = now; |
619 | 672 |
620 stats_.target_media_bitrate_bps = bitrate_bps; | 673 stats_.target_media_bitrate_bps = bitrate_bps; |
621 } | 674 } |
622 | 675 |
| 676 void SendStatisticsProxy::UpdateEncoderFallbackStats( |
| 677 const CodecSpecificInfo* codec_info) { |
| 678 if (!min_first_fallback_interval_ms_ || |
| 679 !uma_container_->fallback_info_.is_possible) { |
| 680 return; |
| 681 } |
| 682 |
| 683 if (!IsForcedFallbackPossible(codec_info)) { |
| 684 uma_container_->fallback_info_.is_possible = false; |
| 685 return; |
| 686 } |
| 687 |
| 688 FallbackEncoderInfo* fallback_info = &uma_container_->fallback_info_; |
| 689 |
| 690 const int64_t now_ms = clock_->TimeInMilliseconds(); |
| 691 bool is_active = fallback_info->is_active; |
| 692 if (codec_info->codec_name != stats_.encoder_implementation_name) { |
| 693 // Implementation changed. |
| 694 is_active = strcmp(codec_info->codec_name, kVp8SwCodecName) == 0; |
| 695 if (!is_active && stats_.encoder_implementation_name != kVp8SwCodecName) { |
| 696 // First or not a VP8 SW change, update stats on next call. |
| 697 return; |
| 698 } |
| 699 if (is_active && fallback_info->on_off_events == 0) { |
| 700 // The minimum set time should have passed for the first fallback (else |
| 701 // skip to avoid fallback due to failure). |
| 702 int64_t elapsed_ms = fallback_info->elapsed_ms; |
| 703 if (fallback_info->last_update_ms) |
| 704 elapsed_ms += now_ms - *(fallback_info->last_update_ms); |
| 705 if (elapsed_ms < *min_first_fallback_interval_ms_) { |
| 706 fallback_info->is_possible = false; |
| 707 return; |
| 708 } |
| 709 } |
| 710 ++fallback_info->on_off_events; |
| 711 } |
| 712 |
| 713 if (fallback_info->last_update_ms) { |
| 714 int64_t diff_ms = now_ms - *(fallback_info->last_update_ms); |
| 715 // If the time diff since last update is greater than |max_frame_diff_ms|, |
| 716 // video is considered paused/muted and the change is not included. |
| 717 if (diff_ms < fallback_info->max_frame_diff_ms) { |
| 718 uma_container_->fallback_active_counter_.Add(fallback_info->is_active, |
| 719 diff_ms); |
| 720 fallback_info->elapsed_ms += diff_ms; |
| 721 } |
| 722 } |
| 723 fallback_info->is_active = is_active; |
| 724 fallback_info->last_update_ms.emplace(now_ms); |
| 725 } |
| 726 |
623 void SendStatisticsProxy::OnSendEncodedImage( | 727 void SendStatisticsProxy::OnSendEncodedImage( |
624 const EncodedImage& encoded_image, | 728 const EncodedImage& encoded_image, |
625 const CodecSpecificInfo* codec_info) { | 729 const CodecSpecificInfo* codec_info) { |
626 size_t simulcast_idx = 0; | 730 size_t simulcast_idx = 0; |
627 | 731 |
628 rtc::CritScope lock(&crit_); | 732 rtc::CritScope lock(&crit_); |
629 ++stats_.frames_encoded; | 733 ++stats_.frames_encoded; |
630 if (codec_info) { | 734 if (codec_info) { |
631 if (codec_info->codecType == kVideoCodecVP8) { | 735 if (codec_info->codecType == kVideoCodecVP8) { |
632 simulcast_idx = codec_info->codecSpecific.VP8.simulcastIdx; | 736 simulcast_idx = codec_info->codecSpecific.VP8.simulcastIdx; |
633 } else if (codec_info->codecType == kVideoCodecGeneric) { | 737 } else if (codec_info->codecType == kVideoCodecGeneric) { |
634 simulcast_idx = codec_info->codecSpecific.generic.simulcast_idx; | 738 simulcast_idx = codec_info->codecSpecific.generic.simulcast_idx; |
635 } | 739 } |
636 if (codec_info->codec_name) { | 740 if (codec_info->codec_name) { |
| 741 UpdateEncoderFallbackStats(codec_info); |
637 stats_.encoder_implementation_name = codec_info->codec_name; | 742 stats_.encoder_implementation_name = codec_info->codec_name; |
638 } | 743 } |
639 } | 744 } |
640 | 745 |
641 if (simulcast_idx >= rtp_config_.ssrcs.size()) { | 746 if (simulcast_idx >= rtp_config_.ssrcs.size()) { |
642 LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx | 747 LOG(LS_ERROR) << "Encoded image outside simulcast range (" << simulcast_idx |
643 << " >= " << rtp_config_.ssrcs.size() << ")."; | 748 << " >= " << rtp_config_.ssrcs.size() << ")."; |
644 return; | 749 return; |
645 } | 750 } |
646 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx]; | 751 uint32_t ssrc = rtp_config_.ssrcs[simulcast_idx]; |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
935 } | 1040 } |
936 | 1041 |
937 int SendStatisticsProxy::BoolSampleCounter::Fraction( | 1042 int SendStatisticsProxy::BoolSampleCounter::Fraction( |
938 int64_t min_required_samples, | 1043 int64_t min_required_samples, |
939 float multiplier) const { | 1044 float multiplier) const { |
940 if (num_samples < min_required_samples || num_samples == 0) | 1045 if (num_samples < min_required_samples || num_samples == 0) |
941 return -1; | 1046 return -1; |
942 return static_cast<int>((sum * multiplier / num_samples) + 0.5f); | 1047 return static_cast<int>((sum * multiplier / num_samples) + 0.5f); |
943 } | 1048 } |
944 } // namespace webrtc | 1049 } // namespace webrtc |
OLD | NEW |