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( |
| 249 &dummy_median, &dummy_std, &fraction_poor_delays) == |
| 250 webrtc::AudioProcessing::kNoError) { |
| 251 echo_fraction_poor_delays_ += fraction_poor_delays; |
| 252 num_queries_++; |
| 253 num_chunks_ = 0; |
| 254 } |
238 LogAecDelayStats(); | 255 LogAecDelayStats(); |
239 } | 256 } |
240 | 257 |
241 void EchoInformation::LogAecDelayStats() { | 258 void EchoInformation::LogAecDelayStats() { |
242 // We update the UMA statistics every 5 seconds. | 259 // We update the UMA statistics every 5 seconds. |
243 const int kTimeBetweenLogsInSeconds = 5; | 260 const int kNumQueriesIn5Seconds = 5; |
244 const base::TimeDelta time_since_last_log = | 261 if (num_queries_ < kNumQueriesIn5Seconds) { |
245 base::TimeTicks::Now() - last_log_time_; | |
246 if (time_since_last_log.InSeconds() < kTimeBetweenLogsInSeconds) | |
247 return; | 262 return; |
| 263 } |
248 | 264 |
249 // Calculate how frequent the AEC delay was out of bounds since last time we | 265 // 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 | 266 // updated UMA histograms by averaging |echo_fraction_poor_delays_| over |
251 // buckets; see DelayBasedEchoQuality. | 267 // |num_queries_|. Then store the result into one of four histogram buckets; |
252 float poor_delay_frequency = 0.f; | 268 // see DelayBasedEchoQuality. |
253 if (echo_total_delay_counts_ > 0) { | 269 float poor_delay_frequency = echo_fraction_poor_delays_ / num_queries_; |
254 poor_delay_frequency = static_cast<float>(echo_poor_delay_counts_) / | 270 UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality", |
255 static_cast<float>(echo_total_delay_counts_); | 271 EchoDelayFrequencyToQuality(poor_delay_frequency), |
256 UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality", | 272 DELAY_BASED_ECHO_QUALITY_MAX); |
257 EchoDelayFrequencyToQuality(poor_delay_frequency), | 273 num_queries_ = 0; |
258 DELAY_BASED_ECHO_QUALITY_MAX); | 274 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 } | 275 } |
264 | 276 |
265 void EnableEchoCancellation(AudioProcessing* audio_processing) { | 277 void EnableEchoCancellation(AudioProcessing* audio_processing) { |
266 #if defined(OS_ANDROID) || defined(OS_IOS) | 278 #if defined(OS_ANDROID) || defined(OS_IOS) |
267 const std::string group_name = | 279 const std::string group_name = |
268 base::FieldTrialList::FindFullName("ReplaceAECMWithAEC"); | 280 base::FieldTrialList::FindFullName("ReplaceAECMWithAEC"); |
269 if (group_name.empty() || | 281 if (group_name.empty() || |
270 !(group_name == "Enabled" || group_name == "DefaultEnabled")) { | 282 !(group_name == "Enabled" || group_name == "DefaultEnabled")) { |
271 // Mobile devices are using AECM. | 283 // Mobile devices are using AECM. |
272 int err = audio_processing->echo_control_mobile()->set_routing_mode( | 284 int err = audio_processing->echo_control_mobile()->set_routing_mode( |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 #if defined(OS_ANDROID) || defined(OS_IOS) | 343 #if defined(OS_ANDROID) || defined(OS_IOS) |
332 const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital; | 344 const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital; |
333 #else | 345 #else |
334 const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog; | 346 const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog; |
335 #endif | 347 #endif |
336 int err = audio_processing->gain_control()->set_mode(mode); | 348 int err = audio_processing->gain_control()->set_mode(mode); |
337 err |= audio_processing->gain_control()->Enable(true); | 349 err |= audio_processing->gain_control()->Enable(true); |
338 CHECK_EQ(err, 0); | 350 CHECK_EQ(err, 0); |
339 } | 351 } |
340 | 352 |
341 void GetAecStats(AudioProcessing* audio_processing, | 353 void GetAecStats(webrtc::EchoCancellation* echo_cancellation, |
342 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) { | 354 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) { |
343 // These values can take on valid negative values, so use the lowest possible | 355 // These values can take on valid negative values, so use the lowest possible |
344 // level as default rather than -1. | 356 // level as default rather than -1. |
345 stats->echo_return_loss = -100; | 357 stats->echo_return_loss = -100; |
346 stats->echo_return_loss_enhancement = -100; | 358 stats->echo_return_loss_enhancement = -100; |
347 | 359 |
348 // These values can also be negative, but in practice -1 is only used to | 360 // 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 | 361 // signal insufficient data, since the resolution is limited to multiples |
350 // of 4ms. | 362 // of 4ms. |
351 stats->echo_delay_median_ms = -1; | 363 stats->echo_delay_median_ms = -1; |
352 stats->echo_delay_std_ms = -1; | 364 stats->echo_delay_std_ms = -1; |
353 | 365 |
354 // TODO(ajm): Re-enable this metric once we have a reliable implementation. | 366 // TODO(ajm): Re-enable this metric once we have a reliable implementation. |
355 stats->aec_quality_min = -1.0f; | 367 stats->aec_quality_min = -1.0f; |
356 | 368 |
357 if (!audio_processing->echo_cancellation()->are_metrics_enabled() || | 369 if (!echo_cancellation->are_metrics_enabled() || |
358 !audio_processing->echo_cancellation()->is_delay_logging_enabled() || | 370 !echo_cancellation->is_delay_logging_enabled() || |
359 !audio_processing->echo_cancellation()->is_enabled()) { | 371 !echo_cancellation->is_enabled()) { |
360 return; | 372 return; |
361 } | 373 } |
362 | 374 |
363 // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary | 375 // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary |
364 // here, but it appears to be unsuitable currently. Revisit after this is | 376 // here, but it appears to be unsuitable currently. Revisit after this is |
365 // investigated: http://b/issue?id=5666755 | 377 // investigated: http://b/issue?id=5666755 |
366 webrtc::EchoCancellation::Metrics echo_metrics; | 378 webrtc::EchoCancellation::Metrics echo_metrics; |
367 if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) { | 379 if (!echo_cancellation->GetMetrics(&echo_metrics)) { |
368 stats->echo_return_loss = echo_metrics.echo_return_loss.instant; | 380 stats->echo_return_loss = echo_metrics.echo_return_loss.instant; |
369 stats->echo_return_loss_enhancement = | 381 stats->echo_return_loss_enhancement = |
370 echo_metrics.echo_return_loss_enhancement.instant; | 382 echo_metrics.echo_return_loss_enhancement.instant; |
371 } | 383 } |
372 | 384 |
373 int median = 0, std = 0; | 385 int median = 0, std = 0; |
374 if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) { | 386 float dummy = 0; |
| 387 if (echo_cancellation->GetDelayMetrics(&median, &std, &dummy) == |
| 388 webrtc::AudioProcessing::kNoError) { |
375 stats->echo_delay_median_ms = median; | 389 stats->echo_delay_median_ms = median; |
376 stats->echo_delay_std_ms = std; | 390 stats->echo_delay_std_ms = std; |
377 } | 391 } |
378 } | 392 } |
379 | 393 |
380 } // namespace content | 394 } // namespace content |
OLD | NEW |