OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/renderer/media/media_stream_audio_processor_options.h" | 5 #include "content/renderer/media/media_stream_audio_processor_options.h" |
6 | 6 |
7 #include "base/files/file_path.h" | 7 #include "base/files/file_path.h" |
8 #include "base/files/file_util.h" | 8 #include "base/files/file_util.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/metrics/field_trial.h" | 10 #include "base/metrics/field_trial.h" |
11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
14 #include "base/time/time.h" | |
15 #include "content/common/media/media_stream_options.h" | 14 #include "content/common/media/media_stream_options.h" |
16 #include "content/renderer/media/media_stream_constraints_util.h" | 15 #include "content/renderer/media/media_stream_constraints_util.h" |
17 #include "content/renderer/media/media_stream_source.h" | 16 #include "content/renderer/media/media_stream_source.h" |
18 #include "content/renderer/media/rtc_media_constraints.h" | 17 #include "content/renderer/media/rtc_media_constraints.h" |
19 #include "media/audio/audio_parameters.h" | 18 #include "media/audio/audio_parameters.h" |
20 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h " | 19 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h " |
21 #include "third_party/webrtc/modules/audio_processing/typing_detection.h" | 20 #include "third_party/webrtc/modules/audio_processing/typing_detection.h" |
22 | 21 |
23 namespace content { | 22 namespace content { |
24 | 23 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
75 bool IsAudioProcessingConstraint(const std::string& key) { | 74 bool IsAudioProcessingConstraint(const std::string& key) { |
76 // |kMediaStreamAudioDucking| does not require audio processing. | 75 // |kMediaStreamAudioDucking| does not require audio processing. |
77 return key != kMediaStreamAudioDucking; | 76 return key != kMediaStreamAudioDucking; |
78 } | 77 } |
79 | 78 |
80 // Used to log echo quality based on delay estimates. | 79 // Used to log echo quality based on delay estimates. |
81 enum DelayBasedEchoQuality { | 80 enum DelayBasedEchoQuality { |
82 DELAY_BASED_ECHO_QUALITY_GOOD = 0, | 81 DELAY_BASED_ECHO_QUALITY_GOOD = 0, |
83 DELAY_BASED_ECHO_QUALITY_SPURIOUS, | 82 DELAY_BASED_ECHO_QUALITY_SPURIOUS, |
84 DELAY_BASED_ECHO_QUALITY_BAD, | 83 DELAY_BASED_ECHO_QUALITY_BAD, |
84 DELAY_BASED_ECHO_QUALITY_INVALID, | |
85 DELAY_BASED_ECHO_QUALITY_MAX | 85 DELAY_BASED_ECHO_QUALITY_MAX |
86 }; | 86 }; |
87 | 87 |
88 DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) { | 88 DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) { |
89 const float kEchoDelayFrequencyLowerLimit = 0.1f; | 89 const float kEchoDelayFrequencyLowerLimit = 0.1f; |
90 const float kEchoDelayFrequencyUpperLimit = 0.8f; | 90 const float kEchoDelayFrequencyUpperLimit = 0.8f; |
91 // DELAY_BASED_ECHO_QUALITY_GOOD | 91 // DELAY_BASED_ECHO_QUALITY_GOOD |
92 // delay is out of bounds during at most 10 % of the time. | 92 // delay is out of bounds during at most 10 % of the time. |
93 // DELAY_BASED_ECHO_QUALITY_SPURIOUS | 93 // DELAY_BASED_ECHO_QUALITY_SPURIOUS |
94 // delay is out of bounds 10-80 % of the time. | 94 // delay is out of bounds 10-80 % of the time. |
95 // DELAY_BASED_ECHO_QUALITY_BAD | 95 // DELAY_BASED_ECHO_QUALITY_BAD |
96 // delay is mostly out of bounds >= 80 % of the time. | 96 // delay is mostly out of bounds >= 80 % of the time. |
97 if (delay_frequency <= kEchoDelayFrequencyLowerLimit) | 97 // DELAY_BASED_ECHO_QUALITY_INVALID |
98 // delay_frequency is negative which happens if we have insufficient data. | |
99 if (delay_frequency < 0) | |
100 return DELAY_BASED_ECHO_QUALITY_INVALID; | |
101 else if (delay_frequency <= kEchoDelayFrequencyLowerLimit) | |
98 return DELAY_BASED_ECHO_QUALITY_GOOD; | 102 return DELAY_BASED_ECHO_QUALITY_GOOD; |
99 else if (delay_frequency < kEchoDelayFrequencyUpperLimit) | 103 else if (delay_frequency < kEchoDelayFrequencyUpperLimit) |
100 return DELAY_BASED_ECHO_QUALITY_SPURIOUS; | 104 return DELAY_BASED_ECHO_QUALITY_SPURIOUS; |
101 else | 105 else |
102 return DELAY_BASED_ECHO_QUALITY_BAD; | 106 return DELAY_BASED_ECHO_QUALITY_BAD; |
103 } | 107 } |
104 | 108 |
105 } // namespace | 109 } // namespace |
106 | 110 |
107 // TODO(xians): Remove this method after the APM in WebRtc is deprecated. | 111 // TODO(xians): Remove this method after the APM in WebRtc is deprecated. |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
207 | 211 |
208 for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) { | 212 for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) { |
209 if (kDefaultAudioConstraints[i].key == key) | 213 if (kDefaultAudioConstraints[i].key == key) |
210 return kDefaultAudioConstraints[i].value; | 214 return kDefaultAudioConstraints[i].value; |
211 } | 215 } |
212 | 216 |
213 return false; | 217 return false; |
214 } | 218 } |
215 | 219 |
216 EchoInformation::EchoInformation() | 220 EchoInformation::EchoInformation() |
217 : echo_poor_delay_counts_(0), | 221 : num_chunks_(0), |
218 echo_total_delay_counts_(0), | 222 num_queries_(0), |
219 last_log_time_(base::TimeTicks::Now()) {} | 223 echo_fraction_poor_delays_(0.0f) {} |
220 | 224 |
221 EchoInformation::~EchoInformation() {} | 225 EchoInformation::~EchoInformation() {} |
222 | 226 |
223 void EchoInformation::UpdateAecDelayStats(int delay) { | 227 void EchoInformation::UpdateAecDelayStats( |
224 // One way to get an indication of how well the echo cancellation performs is | 228 webrtc::EchoCancellation* echo_cancellation) { |
225 // to compare the, by AEC, estimated delay with the AEC filter length. | 229 // In WebRTC, three echo delay metrics are calculated and updated every |
226 // |kMaxAecFilterLengthMs| is the maximum delay we can allow before we | 230 // second. We use one of them, |fraction_poor_delays|, but aggregate over |
227 // consider the AEC to fail. This value should not be larger than the filter | 231 // five seconds to log in a UMA histogram to monitor Echo Cancellation |
228 // length used inside AEC. This is for now set to match the extended filter | 232 // quality. Since the stat in WebRTC has a fixed aggregation window of one |
229 // mode which is turned on for all platforms. | 233 // second we query the stat every second and average over five such queries. |
230 const int kMaxAecFilterLengthMs = 128; | 234 // WebRTC process audio in 10 ms chunks. |
231 if ((delay < -2) || (delay > kMaxAecFilterLengthMs)) { | 235 const int kNumChunksInOneSecond = 100; |
232 // The |delay| is out of bounds which indicates that the echo cancellation | 236 if (!echo_cancellation->is_delay_logging_enabled() || |
233 // filter can not handle the echo. Hence, we have a potential full echo | 237 !echo_cancellation->is_enabled()) { |
234 // case. |delay| values {-1, -2} are reserved for errors. | 238 return; |
235 ++echo_poor_delay_counts_; | |
236 } | 239 } |
237 ++echo_total_delay_counts_; | 240 |
241 num_chunks_++; | |
242 if (num_chunks_ < kNumChunksInOneSecond) { | |
243 return; | |
244 } | |
245 | |
246 int dummy_median = 0, dummy_std = 0; | |
247 float fraction_poor_delays = 0; | |
248 if (!echo_cancellation->GetDelayMetrics(&dummy_median, &dummy_std, | |
perkj_chrome
2015/02/09 11:48:30
Not from this cl I see but it looks quite weird to
bjornv
2015/02/09 14:39:11
Agree. In fact Karl would have killed me for not c
| |
249 &fraction_poor_delays)) { | |
250 echo_fraction_poor_delays_ += fraction_poor_delays; | |
251 num_queries_++; | |
252 num_chunks_ = 0; | |
253 } | |
238 LogAecDelayStats(); | 254 LogAecDelayStats(); |
239 } | 255 } |
240 | 256 |
241 void EchoInformation::LogAecDelayStats() { | 257 void EchoInformation::LogAecDelayStats() { |
242 // We update the UMA statistics every 5 seconds. | 258 // We update the UMA statistics every 5 seconds. |
243 const int kTimeBetweenLogsInSeconds = 5; | 259 const int kNumQueriesIn5Seconds = 5; |
244 const base::TimeDelta time_since_last_log = | 260 if (num_queries_ < kNumQueriesIn5Seconds) { |
245 base::TimeTicks::Now() - last_log_time_; | |
246 if (time_since_last_log.InSeconds() < kTimeBetweenLogsInSeconds) | |
247 return; | 261 return; |
262 } | |
248 | 263 |
249 // Calculate how frequent the AEC delay was out of bounds since last time we | 264 // Calculate how frequent the AEC delay was out of bounds since last time we |
250 // updated UMA histograms. Then store the result into one of three histogram | 265 // updated UMA histograms by averaging |echo_fraction_poor_delays_| over |
251 // buckets; see DelayBasedEchoQuality. | 266 // |num_queries_|. Then store the result into one of four histogram buckets; |
252 float poor_delay_frequency = 0.f; | 267 // see DelayBasedEchoQuality. |
253 if (echo_total_delay_counts_ > 0) { | 268 float poor_delay_frequency = echo_fraction_poor_delays_ / num_queries_; |
254 poor_delay_frequency = static_cast<float>(echo_poor_delay_counts_) / | 269 UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality", |
255 static_cast<float>(echo_total_delay_counts_); | 270 EchoDelayFrequencyToQuality(poor_delay_frequency), |
256 UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality", | 271 DELAY_BASED_ECHO_QUALITY_MAX); |
257 EchoDelayFrequencyToQuality(poor_delay_frequency), | 272 num_queries_ = 0; |
258 DELAY_BASED_ECHO_QUALITY_MAX); | 273 echo_fraction_poor_delays_ = 0.0f; |
259 } | |
260 echo_poor_delay_counts_ = 0; | |
261 echo_total_delay_counts_ = 0; | |
262 last_log_time_ = base::TimeTicks::Now(); | |
263 } | 274 } |
264 | 275 |
265 void EnableEchoCancellation(AudioProcessing* audio_processing) { | 276 void EnableEchoCancellation(AudioProcessing* audio_processing) { |
266 #if defined(OS_ANDROID) || defined(OS_IOS) | 277 #if defined(OS_ANDROID) || defined(OS_IOS) |
267 const std::string group_name = | 278 const std::string group_name = |
268 base::FieldTrialList::FindFullName("ReplaceAECMWithAEC"); | 279 base::FieldTrialList::FindFullName("ReplaceAECMWithAEC"); |
269 if (group_name.empty() || | 280 if (group_name.empty() || |
270 !(group_name == "Enabled" || group_name == "DefaultEnabled")) { | 281 !(group_name == "Enabled" || group_name == "DefaultEnabled")) { |
271 // Mobile devices are using AECM. | 282 // Mobile devices are using AECM. |
272 int err = audio_processing->echo_control_mobile()->set_routing_mode( | 283 int err = audio_processing->echo_control_mobile()->set_routing_mode( |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
338 CHECK_EQ(err, 0); | 349 CHECK_EQ(err, 0); |
339 } | 350 } |
340 | 351 |
341 void GetAecStats(AudioProcessing* audio_processing, | 352 void GetAecStats(AudioProcessing* audio_processing, |
342 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) { | 353 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) { |
343 // These values can take on valid negative values, so use the lowest possible | 354 // These values can take on valid negative values, so use the lowest possible |
344 // level as default rather than -1. | 355 // level as default rather than -1. |
345 stats->echo_return_loss = -100; | 356 stats->echo_return_loss = -100; |
346 stats->echo_return_loss_enhancement = -100; | 357 stats->echo_return_loss_enhancement = -100; |
347 | 358 |
348 // These values can also be negative, but in practice -1 is only used to | 359 // The median value can also be negative, but in practice -1 is only used to |
349 // signal insufficient data, since the resolution is limited to multiples | 360 // signal insufficient data, since the resolution is limited to multiples |
350 // of 4ms. | 361 // of 4ms. |
351 stats->echo_delay_median_ms = -1; | 362 stats->echo_delay_median_ms = -1; |
352 stats->echo_delay_std_ms = -1; | 363 stats->echo_delay_std_ms = -1; |
353 | 364 |
354 // TODO(ajm): Re-enable this metric once we have a reliable implementation. | 365 // TODO(ajm): Re-enable this metric once we have a reliable implementation. |
355 stats->aec_quality_min = -1.0f; | 366 stats->aec_quality_min = -1.0f; |
356 | 367 |
357 if (!audio_processing->echo_cancellation()->are_metrics_enabled() || | 368 if (!audio_processing->echo_cancellation()->are_metrics_enabled() || |
358 !audio_processing->echo_cancellation()->is_delay_logging_enabled() || | 369 !audio_processing->echo_cancellation()->is_delay_logging_enabled() || |
359 !audio_processing->echo_cancellation()->is_enabled()) { | 370 !audio_processing->echo_cancellation()->is_enabled()) { |
360 return; | 371 return; |
361 } | 372 } |
362 | 373 |
363 // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary | 374 // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary |
364 // here, but it appears to be unsuitable currently. Revisit after this is | 375 // here, but it appears to be unsuitable currently. Revisit after this is |
365 // investigated: http://b/issue?id=5666755 | 376 // investigated: http://b/issue?id=5666755 |
366 webrtc::EchoCancellation::Metrics echo_metrics; | 377 webrtc::EchoCancellation::Metrics echo_metrics; |
367 if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) { | 378 if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) { |
368 stats->echo_return_loss = echo_metrics.echo_return_loss.instant; | 379 stats->echo_return_loss = echo_metrics.echo_return_loss.instant; |
369 stats->echo_return_loss_enhancement = | 380 stats->echo_return_loss_enhancement = |
370 echo_metrics.echo_return_loss_enhancement.instant; | 381 echo_metrics.echo_return_loss_enhancement.instant; |
371 } | 382 } |
372 | 383 |
373 int median = 0, std = 0; | 384 int median = 0, std = 0; |
374 if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) { | 385 float dummy = 0; |
386 if (!audio_processing->echo_cancellation()->GetDelayMetrics( | |
387 &median, &std, &dummy)) { | |
375 stats->echo_delay_median_ms = median; | 388 stats->echo_delay_median_ms = median; |
376 stats->echo_delay_std_ms = std; | 389 stats->echo_delay_std_ms = std; |
377 } | 390 } |
378 } | 391 } |
379 | 392 |
380 } // namespace content | 393 } // namespace content |
OLD | NEW |