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
3 // found in the LICENSE file.
5 #include "content/renderer/media/media_stream_audio_processor_options.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/logging.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_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"
16 #include "content/renderer/media/media_stream_constraints_util.h"
17 #include "content/renderer/media/media_stream_source.h"
18 #include "content/renderer/media/rtc_media_constraints.h"
19 #include "media/audio/audio_parameters.h"
20 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
21 #include "third_party/webrtc/modules/audio_processing/typing_detection.h"
25 const char MediaAudioConstraints::kEchoCancellation[] = "echoCancellation";
26 const char MediaAudioConstraints::kGoogEchoCancellation[] =
27 "googEchoCancellation";
28 const char MediaAudioConstraints::kGoogExperimentalEchoCancellation[] =
29 "googEchoCancellation2";
30 const char MediaAudioConstraints::kGoogAutoGainControl[] =
31 "googAutoGainControl";
32 const char MediaAudioConstraints::kGoogExperimentalAutoGainControl[] =
33 "googAutoGainControl2";
34 const char MediaAudioConstraints::kGoogNoiseSuppression[] =
35 "googNoiseSuppression";
36 const char MediaAudioConstraints::kGoogExperimentalNoiseSuppression[] =
37 "googNoiseSuppression2";
38 const char MediaAudioConstraints::kGoogHighpassFilter[] = "googHighpassFilter";
39 const char MediaAudioConstraints::kGoogTypingNoiseDetection[] =
40 "googTypingNoiseDetection";
41 const char MediaAudioConstraints::kGoogAudioMirroring[] = "googAudioMirroring";
45 // Constant constraint keys which enables default audio constraints on
46 // mediastreams with audio.
50 } const kDefaultAudioConstraints[] = {
51 { MediaAudioConstraints::kEchoCancellation, true },
52 { MediaAudioConstraints::kGoogEchoCancellation, true },
53 #if defined(OS_ANDROID) || defined(OS_IOS)
54 { MediaAudioConstraints::kGoogExperimentalEchoCancellation, false },
56 // Enable the extended filter mode AEC on all non-mobile platforms.
57 { MediaAudioConstraints::kGoogExperimentalEchoCancellation, true },
59 { MediaAudioConstraints::kGoogAutoGainControl, true },
60 { MediaAudioConstraints::kGoogExperimentalAutoGainControl, true },
61 { MediaAudioConstraints::kGoogNoiseSuppression, true },
62 { MediaAudioConstraints::kGoogHighpassFilter, true },
63 { MediaAudioConstraints::kGoogTypingNoiseDetection, true },
64 { MediaAudioConstraints::kGoogExperimentalNoiseSuppression, false },
66 { kMediaStreamAudioDucking, true },
68 { kMediaStreamAudioDucking, false },
72 bool IsAudioProcessingConstraint(const std::string& key) {
73 // |kMediaStreamAudioDucking| does not require audio processing.
74 return key != kMediaStreamAudioDucking;
77 // Used to log echo quality based on delay estimates.
78 enum DelayBasedEchoQuality {
79 DELAY_BASED_ECHO_QUALITY_GOOD = 0,
80 DELAY_BASED_ECHO_QUALITY_SPURIOUS,
81 DELAY_BASED_ECHO_QUALITY_BAD,
82 DELAY_BASED_ECHO_QUALITY_MAX
85 DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) {
86 const float kEchoDelayFrequencyLowerLimit = 0.1f;
87 const float kEchoDelayFrequencyUpperLimit = 0.8f;
88 // DELAY_BASED_ECHO_QUALITY_GOOD
89 // delay is out of bounds during at most 10 % of the time.
90 // DELAY_BASED_ECHO_QUALITY_SPURIOUS
91 // delay is out of bounds 10-80 % of the time.
92 // DELAY_BASED_ECHO_QUALITY_BAD
93 // delay is mostly out of bounds >= 80 % of the time.
94 if (delay_frequency <= kEchoDelayFrequencyLowerLimit)
95 return DELAY_BASED_ECHO_QUALITY_GOOD;
96 else if (delay_frequency < kEchoDelayFrequencyUpperLimit)
97 return DELAY_BASED_ECHO_QUALITY_SPURIOUS;
99 return DELAY_BASED_ECHO_QUALITY_BAD;
104 // TODO(xians): Remove this method after the APM in WebRtc is deprecated.
105 void MediaAudioConstraints::ApplyFixedAudioConstraints(
106 RTCMediaConstraints* constraints) {
107 for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) {
108 bool already_set_value;
109 if (!webrtc::FindConstraint(constraints, kDefaultAudioConstraints[i].key,
110 &already_set_value, NULL)) {
111 const std::string value = kDefaultAudioConstraints[i].value ?
112 webrtc::MediaConstraintsInterface::kValueTrue :
113 webrtc::MediaConstraintsInterface::kValueFalse;
114 constraints->AddOptional(kDefaultAudioConstraints[i].key, value, false);
116 DVLOG(1) << "Constraint " << kDefaultAudioConstraints[i].key
117 << " already set to " << already_set_value;
122 MediaAudioConstraints::MediaAudioConstraints(
123 const blink::WebMediaConstraints& constraints, int effects)
124 : constraints_(constraints),
126 default_audio_processing_constraint_value_(true) {
127 // The default audio processing constraints are turned off when
128 // - gUM has a specific kMediaStreamSource, which is used by tab capture
129 // and screen capture.
130 // - |kEchoCancellation| is explicitly set to false.
131 std::string value_str;
132 bool value_bool = false;
133 if ((GetConstraintValueAsString(constraints, kMediaStreamSource,
135 (GetConstraintValueAsBoolean(constraints_, kEchoCancellation,
136 &value_bool) && !value_bool)) {
137 default_audio_processing_constraint_value_ = false;
141 MediaAudioConstraints::~MediaAudioConstraints() {}
143 // TODO(xians): Remove this method after the APM in WebRtc is deprecated.
144 bool MediaAudioConstraints::NeedsAudioProcessing() {
145 if (GetEchoCancellationProperty())
148 for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) {
149 // |kEchoCancellation| and |kGoogEchoCancellation| have been convered by
150 // GetEchoCancellationProperty().
151 if (kDefaultAudioConstraints[i].key != kEchoCancellation &&
152 kDefaultAudioConstraints[i].key != kGoogEchoCancellation &&
153 IsAudioProcessingConstraint(kDefaultAudioConstraints[i].key) &&
154 GetProperty(kDefaultAudioConstraints[i].key)) {
162 bool MediaAudioConstraints::GetProperty(const std::string& key) {
163 // Return the value if the constraint is specified in |constraints|,
164 // otherwise return the default value.
166 if (!GetConstraintValueAsBoolean(constraints_, key, &value))
167 value = GetDefaultValueForConstraint(constraints_, key);
172 bool MediaAudioConstraints::GetEchoCancellationProperty() {
173 // If platform echo canceller is enabled, disable the software AEC.
174 if (effects_ & media::AudioParameters::ECHO_CANCELLER)
177 // If |kEchoCancellation| is specified in the constraints, it will
178 // override the value of |kGoogEchoCancellation|.
180 if (GetConstraintValueAsBoolean(constraints_, kEchoCancellation, &value))
183 return GetProperty(kGoogEchoCancellation);
186 bool MediaAudioConstraints::IsValid() {
187 blink::WebVector<blink::WebMediaConstraint> mandatory;
188 constraints_.getMandatoryConstraints(mandatory);
189 for (size_t i = 0; i < mandatory.size(); ++i) {
190 const std::string key = mandatory[i].m_name.utf8();
191 if (key == kMediaStreamSource || key == kMediaStreamSourceId ||
192 key == MediaStreamSource::kSourceId) {
193 // Ignore Chrome specific Tab capture and |kSourceId| constraints.
198 for (size_t j = 0; j < arraysize(kDefaultAudioConstraints); ++j) {
199 if (key == kDefaultAudioConstraints[j].key) {
201 valid = GetMandatoryConstraintValueAsBoolean(constraints_, key, &value);
207 DLOG(ERROR) << "Invalid MediaStream constraint. Name: " << key;
215 bool MediaAudioConstraints::GetDefaultValueForConstraint(
216 const blink::WebMediaConstraints& constraints, const std::string& key) {
217 // |kMediaStreamAudioDucking| is not restricted by
218 // |default_audio_processing_constraint_value_| since it does not require
220 if (!default_audio_processing_constraint_value_ &&
221 IsAudioProcessingConstraint(key))
224 for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) {
225 if (kDefaultAudioConstraints[i].key == key)
226 return kDefaultAudioConstraints[i].value;
232 EchoInformation::EchoInformation()
233 : echo_poor_delay_counts_(0),
234 echo_total_delay_counts_(0),
235 last_log_time_(base::TimeTicks::Now()) {}
237 EchoInformation::~EchoInformation() {}
239 void EchoInformation::UpdateAecDelayStats(int delay) {
240 // One way to get an indication of how well the echo cancellation performs is
241 // to compare the, by AEC, estimated delay with the AEC filter length.
242 // |kMaxAecFilterLengthMs| is the maximum delay we can allow before we
243 // consider the AEC to fail. This value should not be larger than the filter
244 // length used inside AEC. This is for now set to match the extended filter
245 // mode which is turned on for all platforms.
246 const int kMaxAecFilterLengthMs = 128;
247 if ((delay < -2) || (delay > kMaxAecFilterLengthMs)) {
248 // The |delay| is out of bounds which indicates that the echo cancellation
249 // filter can not handle the echo. Hence, we have a potential full echo
250 // case. |delay| values {-1, -2} are reserved for errors.
251 ++echo_poor_delay_counts_;
253 ++echo_total_delay_counts_;
257 void EchoInformation::LogAecDelayStats() {
258 // We update the UMA statistics every 5 seconds.
259 const int kTimeBetweenLogsInSeconds = 5;
260 const base::TimeDelta time_since_last_log =
261 base::TimeTicks::Now() - last_log_time_;
262 if (time_since_last_log.InSeconds() < kTimeBetweenLogsInSeconds)
265 // Calculate how frequent the AEC delay was out of bounds since last time we
266 // updated UMA histograms. Then store the result into one of three histogram
267 // buckets; see DelayBasedEchoQuality.
268 float poor_delay_frequency = 0.f;
269 if (echo_total_delay_counts_ > 0) {
270 poor_delay_frequency = static_cast<float>(echo_poor_delay_counts_) /
271 static_cast<float>(echo_total_delay_counts_);
272 UMA_HISTOGRAM_ENUMERATION("Media.AecDelayBasedQuality",
273 EchoDelayFrequencyToQuality(poor_delay_frequency),
274 DELAY_BASED_ECHO_QUALITY_MAX);
276 echo_poor_delay_counts_ = 0;
277 echo_total_delay_counts_ = 0;
278 last_log_time_ = base::TimeTicks::Now();
281 void EnableEchoCancellation(AudioProcessing* audio_processing) {
282 #if defined(OS_ANDROID) || defined(OS_IOS)
283 const std::string group_name =
284 base::FieldTrialList::FindFullName("ReplaceAECMWithAEC");
285 if (group_name.empty() || group_name != "Enabled") {
286 // Mobile devices are using AECM.
287 int err = audio_processing->echo_control_mobile()->set_routing_mode(
288 webrtc::EchoControlMobile::kSpeakerphone);
289 err |= audio_processing->echo_control_mobile()->Enable(true);
294 int err = audio_processing->echo_cancellation()->set_suppression_level(
295 webrtc::EchoCancellation::kHighSuppression);
297 // Enable the metrics for AEC.
298 err |= audio_processing->echo_cancellation()->enable_metrics(true);
299 err |= audio_processing->echo_cancellation()->enable_delay_logging(true);
300 err |= audio_processing->echo_cancellation()->Enable(true);
304 void EnableNoiseSuppression(AudioProcessing* audio_processing) {
305 int err = audio_processing->noise_suppression()->set_level(
306 webrtc::NoiseSuppression::kHigh);
307 err |= audio_processing->noise_suppression()->Enable(true);
311 void EnableHighPassFilter(AudioProcessing* audio_processing) {
312 CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0);
315 void EnableTypingDetection(AudioProcessing* audio_processing,
316 webrtc::TypingDetection* typing_detector) {
317 int err = audio_processing->voice_detection()->Enable(true);
318 err |= audio_processing->voice_detection()->set_likelihood(
319 webrtc::VoiceDetection::kVeryLowLikelihood);
322 // Configure the update period to 1s (100 * 10ms) in the typing detector.
323 typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
326 void StartEchoCancellationDump(AudioProcessing* audio_processing,
327 base::File aec_dump_file) {
328 DCHECK(aec_dump_file.IsValid());
330 FILE* stream = base::FileToFILE(aec_dump_file.Pass(), "w");
332 LOG(ERROR) << "Failed to open AEC dump file";
336 if (audio_processing->StartDebugRecording(stream))
337 DLOG(ERROR) << "Fail to start AEC debug recording";
340 void StopEchoCancellationDump(AudioProcessing* audio_processing) {
341 if (audio_processing->StopDebugRecording())
342 DLOG(ERROR) << "Fail to stop AEC debug recording";
345 void EnableAutomaticGainControl(AudioProcessing* audio_processing) {
346 #if defined(OS_ANDROID) || defined(OS_IOS)
347 const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital;
349 const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog;
351 int err = audio_processing->gain_control()->set_mode(mode);
352 err |= audio_processing->gain_control()->Enable(true);
356 void GetAecStats(AudioProcessing* audio_processing,
357 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) {
358 // These values can take on valid negative values, so use the lowest possible
359 // level as default rather than -1.
360 stats->echo_return_loss = -100;
361 stats->echo_return_loss_enhancement = -100;
363 // These values can also be negative, but in practice -1 is only used to
364 // signal insufficient data, since the resolution is limited to multiples
366 stats->echo_delay_median_ms = -1;
367 stats->echo_delay_std_ms = -1;
369 // TODO(ajm): Re-enable this metric once we have a reliable implementation.
370 stats->aec_quality_min = -1.0f;
372 if (!audio_processing->echo_cancellation()->are_metrics_enabled() ||
373 !audio_processing->echo_cancellation()->is_delay_logging_enabled() ||
374 !audio_processing->echo_cancellation()->is_enabled()) {
378 // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary
379 // here, but it appears to be unsuitable currently. Revisit after this is
380 // investigated: http://b/issue?id=5666755
381 webrtc::EchoCancellation::Metrics echo_metrics;
382 if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) {
383 stats->echo_return_loss = echo_metrics.echo_return_loss.instant;
384 stats->echo_return_loss_enhancement =
385 echo_metrics.echo_return_loss_enhancement.instant;
388 int median = 0, std = 0;
389 if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) {
390 stats->echo_delay_median_ms = median;
391 stats->echo_delay_std_ms = std;
395 } // namespace content