Chromium Code Reviews| 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 |