Index: content/renderer/media/media_stream_audio_processor_options.cc |
diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc |
index b2465b3350286d05a8f543b8c11bf04455a0bca5..ccba777e9549e7624308c38050debe3680df1bd7 100644 |
--- a/content/renderer/media/media_stream_audio_processor_options.cc |
+++ b/content/renderer/media/media_stream_audio_processor_options.cc |
@@ -11,7 +11,6 @@ |
#include "base/metrics/histogram.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/utf_string_conversions.h" |
-#include "base/time/time.h" |
#include "content/common/media/media_stream_options.h" |
#include "content/renderer/media/media_stream_constraints_util.h" |
#include "content/renderer/media/media_stream_source.h" |
@@ -82,6 +81,7 @@ enum DelayBasedEchoQuality { |
DELAY_BASED_ECHO_QUALITY_GOOD = 0, |
DELAY_BASED_ECHO_QUALITY_SPURIOUS, |
DELAY_BASED_ECHO_QUALITY_BAD, |
+ DELAY_BASED_ECHO_QUALITY_INVALID, |
DELAY_BASED_ECHO_QUALITY_MAX |
}; |
@@ -94,7 +94,11 @@ DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) { |
// delay is out of bounds 10-80 % of the time. |
// DELAY_BASED_ECHO_QUALITY_BAD |
// delay is mostly out of bounds >= 80 % of the time. |
- if (delay_frequency <= kEchoDelayFrequencyLowerLimit) |
+ // DELAY_BASED_ECHO_QUALITY_INVALID |
+ // delay_frequency is negative which happens if we have insufficient data. |
+ if (delay_frequency < 0) |
+ return DELAY_BASED_ECHO_QUALITY_INVALID; |
+ else if (delay_frequency <= kEchoDelayFrequencyLowerLimit) |
return DELAY_BASED_ECHO_QUALITY_GOOD; |
else if (delay_frequency < kEchoDelayFrequencyUpperLimit) |
return DELAY_BASED_ECHO_QUALITY_SPURIOUS; |
@@ -214,52 +218,60 @@ bool MediaAudioConstraints::GetDefaultValueForConstraint( |
} |
EchoInformation::EchoInformation() |
- : echo_poor_delay_counts_(0), |
- echo_total_delay_counts_(0), |
- last_log_time_(base::TimeTicks::Now()) {} |
+ : num_chunks_(0), |
+ num_queries_(0), |
+ echo_fraction_poor_delays_(0.0f) {} |
EchoInformation::~EchoInformation() {} |
-void EchoInformation::UpdateAecDelayStats(int delay) { |
- // One way to get an indication of how well the echo cancellation performs is |
- // to compare the, by AEC, estimated delay with the AEC filter length. |
- // |kMaxAecFilterLengthMs| is the maximum delay we can allow before we |
- // consider the AEC to fail. This value should not be larger than the filter |
- // length used inside AEC. This is for now set to match the extended filter |
- // mode which is turned on for all platforms. |
- const int kMaxAecFilterLengthMs = 128; |
- if ((delay < -2) || (delay > kMaxAecFilterLengthMs)) { |
- // The |delay| is out of bounds which indicates that the echo cancellation |
- // filter can not handle the echo. Hence, we have a potential full echo |
- // case. |delay| values {-1, -2} are reserved for errors. |
- ++echo_poor_delay_counts_; |
+void EchoInformation::UpdateAecDelayStats( |
+ webrtc::EchoCancellation* echo_cancellation) { |
+ // In WebRTC, three echo delay metrics are calculated and updated every |
+ // second. We use one of them, |fraction_poor_delays|, but aggregate over |
+ // five seconds to log in a UMA histogram to monitor Echo Cancellation |
+ // quality. Since the stat in WebRTC has a fixed aggregation window of one |
+ // second we query the stat every second and average over five such queries. |
+ // WebRTC process audio in 10 ms chunks. |
+ const int kNumChunksInOneSecond = 100; |
+ if (!echo_cancellation->is_delay_logging_enabled() || |
+ !echo_cancellation->is_enabled()) { |
+ return; |
+ } |
+ |
+ num_chunks_++; |
+ if (num_chunks_ < kNumChunksInOneSecond) { |
+ return; |
+ } |
+ |
+ int dummy_median = 0, dummy_std = 0; |
+ float fraction_poor_delays = 0; |
+ if (echo_cancellation->GetDelayMetrics( |
+ &dummy_median, &dummy_std, &fraction_poor_delays) == |
+ webrtc::AudioProcessing::kNoError) { |
+ echo_fraction_poor_delays_ += fraction_poor_delays; |
+ num_queries_++; |
+ num_chunks_ = 0; |
} |
- ++echo_total_delay_counts_; |
LogAecDelayStats(); |
} |
void EchoInformation::LogAecDelayStats() { |
// We update the UMA statistics every 5 seconds. |
- const int kTimeBetweenLogsInSeconds = 5; |
- const base::TimeDelta time_since_last_log = |
- base::TimeTicks::Now() - last_log_time_; |
- if (time_since_last_log.InSeconds() < kTimeBetweenLogsInSeconds) |
+ const int kNumQueriesIn5Seconds = 5; |
+ if (num_queries_ < kNumQueriesIn5Seconds) { |
return; |
+ } |
// Calculate how frequent the AEC delay was out of bounds since last time we |
- // updated UMA histograms. Then store the result into one of three histogram |
- // buckets; see DelayBasedEchoQuality. |
- float poor_delay_frequency = 0.f; |
- if (echo_total_delay_counts_ > 0) { |
- poor_delay_frequency = static_cast<float>(echo_poor_delay_counts_) / |
- static_cast<float>(echo_total_delay_counts_); |
- UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality", |
- EchoDelayFrequencyToQuality(poor_delay_frequency), |
- DELAY_BASED_ECHO_QUALITY_MAX); |
- } |
- echo_poor_delay_counts_ = 0; |
- echo_total_delay_counts_ = 0; |
- last_log_time_ = base::TimeTicks::Now(); |
+ // updated UMA histograms by averaging |echo_fraction_poor_delays_| over |
+ // |num_queries_|. Then store the result into one of four histogram buckets; |
+ // see DelayBasedEchoQuality. |
+ float poor_delay_frequency = echo_fraction_poor_delays_ / num_queries_; |
+ UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality", |
+ EchoDelayFrequencyToQuality(poor_delay_frequency), |
+ DELAY_BASED_ECHO_QUALITY_MAX); |
+ num_queries_ = 0; |
+ echo_fraction_poor_delays_ = 0.0f; |
} |
void EnableEchoCancellation(AudioProcessing* audio_processing) { |
@@ -338,14 +350,14 @@ void EnableAutomaticGainControl(AudioProcessing* audio_processing) { |
CHECK_EQ(err, 0); |
} |
-void GetAecStats(AudioProcessing* audio_processing, |
+void GetAecStats(webrtc::EchoCancellation* echo_cancellation, |
webrtc::AudioProcessorInterface::AudioProcessorStats* stats) { |
// These values can take on valid negative values, so use the lowest possible |
// level as default rather than -1. |
stats->echo_return_loss = -100; |
stats->echo_return_loss_enhancement = -100; |
- // These values can also be negative, but in practice -1 is only used to |
+ // The median value can also be negative, but in practice -1 is only used to |
// signal insufficient data, since the resolution is limited to multiples |
// of 4ms. |
stats->echo_delay_median_ms = -1; |
@@ -354,9 +366,9 @@ void GetAecStats(AudioProcessing* audio_processing, |
// TODO(ajm): Re-enable this metric once we have a reliable implementation. |
stats->aec_quality_min = -1.0f; |
- if (!audio_processing->echo_cancellation()->are_metrics_enabled() || |
- !audio_processing->echo_cancellation()->is_delay_logging_enabled() || |
- !audio_processing->echo_cancellation()->is_enabled()) { |
+ if (!echo_cancellation->are_metrics_enabled() || |
+ !echo_cancellation->is_delay_logging_enabled() || |
+ !echo_cancellation->is_enabled()) { |
return; |
} |
@@ -364,14 +376,16 @@ void GetAecStats(AudioProcessing* audio_processing, |
// here, but it appears to be unsuitable currently. Revisit after this is |
// investigated: http://b/issue?id=5666755 |
webrtc::EchoCancellation::Metrics echo_metrics; |
- if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) { |
+ if (!echo_cancellation->GetMetrics(&echo_metrics)) { |
stats->echo_return_loss = echo_metrics.echo_return_loss.instant; |
stats->echo_return_loss_enhancement = |
echo_metrics.echo_return_loss_enhancement.instant; |
} |
int median = 0, std = 0; |
- if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) { |
+ float dummy = 0; |
+ if (echo_cancellation->GetDelayMetrics(&median, &std, &dummy) == |
+ webrtc::AudioProcessing::kNoError) { |
stats->echo_delay_median_ms = median; |
stats->echo_delay_std_ms = std; |
} |