1 /*
2 * Copyright (c) 2021 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 "modules/audio_processing/agc2/input_volume_stats_reporter.h"
12
13 #include <cmath>
14
15 #include "absl/strings/string_view.h"
16 #include "rtc_base/logging.h"
17 #include "rtc_base/numerics/safe_minmax.h"
18 #include "rtc_base/strings/string_builder.h"
19 #include "system_wrappers/include/metrics.h"
20
21 namespace webrtc {
22 namespace {
23
24 using InputVolumeType = InputVolumeStatsReporter::InputVolumeType;
25
26 constexpr int kFramesIn60Seconds = 6000;
27 constexpr int kMinInputVolume = 0;
28 constexpr int kMaxInputVolume = 255;
29 constexpr int kMaxUpdate = kMaxInputVolume - kMinInputVolume;
30
ComputeAverageUpdate(int sum_updates,int num_updates)31 int ComputeAverageUpdate(int sum_updates, int num_updates) {
32 RTC_DCHECK_GE(sum_updates, 0);
33 RTC_DCHECK_LE(sum_updates, kMaxUpdate * kFramesIn60Seconds);
34 RTC_DCHECK_GE(num_updates, 0);
35 RTC_DCHECK_LE(num_updates, kFramesIn60Seconds);
36 if (num_updates == 0) {
37 return 0;
38 }
39 return std::round(static_cast<float>(sum_updates) /
40 static_cast<float>(num_updates));
41 }
42
MetricNamePrefix(InputVolumeType input_volume_type)43 constexpr absl::string_view MetricNamePrefix(
44 InputVolumeType input_volume_type) {
45 switch (input_volume_type) {
46 case InputVolumeType::kApplied:
47 return "WebRTC.Audio.Apm.AppliedInputVolume.";
48 case InputVolumeType::kRecommended:
49 return "WebRTC.Audio.Apm.RecommendedInputVolume.";
50 }
51 }
52
CreateRateHistogram(InputVolumeType input_volume_type,absl::string_view name)53 metrics::Histogram* CreateRateHistogram(InputVolumeType input_volume_type,
54 absl::string_view name) {
55 char buffer[64];
56 rtc::SimpleStringBuilder builder(buffer);
57 builder << MetricNamePrefix(input_volume_type) << name;
58 return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(),
59 /*min=*/1,
60 /*max=*/kFramesIn60Seconds,
61 /*bucket_count=*/50);
62 }
63
CreateAverageHistogram(InputVolumeType input_volume_type,absl::string_view name)64 metrics::Histogram* CreateAverageHistogram(InputVolumeType input_volume_type,
65 absl::string_view name) {
66 char buffer[64];
67 rtc::SimpleStringBuilder builder(buffer);
68 builder << MetricNamePrefix(input_volume_type) << name;
69 return metrics::HistogramFactoryGetCountsLinear(/*name=*/builder.str(),
70 /*min=*/1,
71 /*max=*/kMaxUpdate,
72 /*bucket_count=*/50);
73 }
74
75 } // namespace
76
InputVolumeStatsReporter(InputVolumeType type)77 InputVolumeStatsReporter::InputVolumeStatsReporter(InputVolumeType type)
78 : histograms_(
79 {.decrease_rate = CreateRateHistogram(type, "DecreaseRate"),
80 .decrease_average = CreateAverageHistogram(type, "DecreaseAverage"),
81 .increase_rate = CreateRateHistogram(type, "IncreaseRate"),
82 .increase_average = CreateAverageHistogram(type, "IncreaseAverage"),
83 .update_rate = CreateRateHistogram(type, "UpdateRate"),
84 .update_average = CreateAverageHistogram(type, "UpdateAverage")}),
85 cannot_log_stats_(!histograms_.AllPointersSet()) {
86 if (cannot_log_stats_) {
87 RTC_LOG(LS_WARNING) << "Will not log any `" << MetricNamePrefix(type)
88 << "*` histogram stats.";
89 }
90 }
91
92 InputVolumeStatsReporter::~InputVolumeStatsReporter() = default;
93
UpdateStatistics(int input_volume)94 void InputVolumeStatsReporter::UpdateStatistics(int input_volume) {
95 if (cannot_log_stats_) {
96 // Since the stats cannot be logged, do not bother updating them.
97 return;
98 }
99
100 RTC_DCHECK_GE(input_volume, kMinInputVolume);
101 RTC_DCHECK_LE(input_volume, kMaxInputVolume);
102 if (previous_input_volume_.has_value() &&
103 input_volume != previous_input_volume_.value()) {
104 const int volume_change = input_volume - previous_input_volume_.value();
105 if (volume_change < 0) {
106 ++volume_update_stats_.num_decreases;
107 volume_update_stats_.sum_decreases -= volume_change;
108 } else {
109 ++volume_update_stats_.num_increases;
110 volume_update_stats_.sum_increases += volume_change;
111 }
112 }
113 // Periodically log input volume change metrics.
114 if (++log_volume_update_stats_counter_ >= kFramesIn60Seconds) {
115 LogVolumeUpdateStats();
116 volume_update_stats_ = {};
117 log_volume_update_stats_counter_ = 0;
118 }
119 previous_input_volume_ = input_volume;
120 }
121
LogVolumeUpdateStats() const122 void InputVolumeStatsReporter::LogVolumeUpdateStats() const {
123 // Decrease rate and average.
124 metrics::HistogramAdd(histograms_.decrease_rate,
125 volume_update_stats_.num_decreases);
126 if (volume_update_stats_.num_decreases > 0) {
127 int average_decrease = ComputeAverageUpdate(
128 volume_update_stats_.sum_decreases, volume_update_stats_.num_decreases);
129 metrics::HistogramAdd(histograms_.decrease_average, average_decrease);
130 }
131 // Increase rate and average.
132 metrics::HistogramAdd(histograms_.increase_rate,
133 volume_update_stats_.num_increases);
134 if (volume_update_stats_.num_increases > 0) {
135 int average_increase = ComputeAverageUpdate(
136 volume_update_stats_.sum_increases, volume_update_stats_.num_increases);
137 metrics::HistogramAdd(histograms_.increase_average, average_increase);
138 }
139 // Update rate and average.
140 int num_updates =
141 volume_update_stats_.num_decreases + volume_update_stats_.num_increases;
142 metrics::HistogramAdd(histograms_.update_rate, num_updates);
143 if (num_updates > 0) {
144 int average_update = ComputeAverageUpdate(
145 volume_update_stats_.sum_decreases + volume_update_stats_.sum_increases,
146 num_updates);
147 metrics::HistogramAdd(histograms_.update_average, average_update);
148 }
149 }
150
151 } // namespace webrtc
152