Roll src/third_party/WebKit a452221:9ff6d11 (svn 202117:202119)
[chromium-blink-merge.git] / content / renderer / media / media_stream_audio_processor_options.cc
blob5c61a055cc47fd2e1bc014e2e385ad4af4710c9d
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/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "content/common/media/media_stream_options.h"
17 #include "content/renderer/media/media_stream_constraints_util.h"
18 #include "content/renderer/media/media_stream_source.h"
19 #include "content/renderer/media/rtc_media_constraints.h"
20 #include "media/audio/audio_parameters.h"
21 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
22 #include "third_party/webrtc/modules/audio_processing/typing_detection.h"
24 namespace content {
26 const char MediaAudioConstraints::kEchoCancellation[] = "echoCancellation";
27 const char MediaAudioConstraints::kGoogEchoCancellation[] =
28 "googEchoCancellation";
29 const char MediaAudioConstraints::kGoogExperimentalEchoCancellation[] =
30 "googEchoCancellation2";
31 const char MediaAudioConstraints::kGoogAutoGainControl[] =
32 "googAutoGainControl";
33 const char MediaAudioConstraints::kGoogExperimentalAutoGainControl[] =
34 "googAutoGainControl2";
35 const char MediaAudioConstraints::kGoogNoiseSuppression[] =
36 "googNoiseSuppression";
37 const char MediaAudioConstraints::kGoogExperimentalNoiseSuppression[] =
38 "googNoiseSuppression2";
39 const char MediaAudioConstraints::kGoogBeamforming[] = "googBeamforming";
40 const char MediaAudioConstraints::kGoogArrayGeometry[] = "googArrayGeometry";
41 const char MediaAudioConstraints::kGoogHighpassFilter[] = "googHighpassFilter";
42 const char MediaAudioConstraints::kGoogTypingNoiseDetection[] =
43 "googTypingNoiseDetection";
44 const char MediaAudioConstraints::kGoogAudioMirroring[] = "googAudioMirroring";
46 namespace {
48 // Constant constraint keys which enables default audio constraints on
49 // mediastreams with audio.
50 struct {
51 const char* key;
52 bool value;
53 } const kDefaultAudioConstraints[] = {
54 { MediaAudioConstraints::kEchoCancellation, true },
55 { MediaAudioConstraints::kGoogEchoCancellation, true },
56 #if defined(OS_ANDROID) || defined(OS_IOS)
57 { MediaAudioConstraints::kGoogExperimentalEchoCancellation, false },
58 #else
59 // Enable the extended filter mode AEC on all non-mobile platforms.
60 { MediaAudioConstraints::kGoogExperimentalEchoCancellation, true },
61 #endif
62 { MediaAudioConstraints::kGoogAutoGainControl, true },
63 { MediaAudioConstraints::kGoogExperimentalAutoGainControl, true },
64 { MediaAudioConstraints::kGoogNoiseSuppression, true },
65 { MediaAudioConstraints::kGoogHighpassFilter, true },
66 { MediaAudioConstraints::kGoogTypingNoiseDetection, true },
67 { MediaAudioConstraints::kGoogExperimentalNoiseSuppression, false },
68 { MediaAudioConstraints::kGoogBeamforming, false },
69 { kMediaStreamAudioHotword, false },
72 // Used to log echo quality based on delay estimates.
73 enum DelayBasedEchoQuality {
74 DELAY_BASED_ECHO_QUALITY_GOOD = 0,
75 DELAY_BASED_ECHO_QUALITY_SPURIOUS,
76 DELAY_BASED_ECHO_QUALITY_BAD,
77 DELAY_BASED_ECHO_QUALITY_INVALID,
78 DELAY_BASED_ECHO_QUALITY_MAX
81 DelayBasedEchoQuality EchoDelayFrequencyToQuality(float delay_frequency) {
82 const float kEchoDelayFrequencyLowerLimit = 0.1f;
83 const float kEchoDelayFrequencyUpperLimit = 0.8f;
84 // DELAY_BASED_ECHO_QUALITY_GOOD
85 // delay is out of bounds during at most 10 % of the time.
86 // DELAY_BASED_ECHO_QUALITY_SPURIOUS
87 // delay is out of bounds 10-80 % of the time.
88 // DELAY_BASED_ECHO_QUALITY_BAD
89 // delay is mostly out of bounds >= 80 % of the time.
90 // DELAY_BASED_ECHO_QUALITY_INVALID
91 // delay_frequency is negative which happens if we have insufficient data.
92 if (delay_frequency < 0)
93 return DELAY_BASED_ECHO_QUALITY_INVALID;
94 else if (delay_frequency <= kEchoDelayFrequencyLowerLimit)
95 return DELAY_BASED_ECHO_QUALITY_GOOD;
96 else if (delay_frequency < kEchoDelayFrequencyUpperLimit)
97 return DELAY_BASED_ECHO_QUALITY_SPURIOUS;
98 else
99 return DELAY_BASED_ECHO_QUALITY_BAD;
102 } // namespace
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);
115 } else {
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),
125 effects_(effects),
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,
134 &value_str)) ||
135 (GetConstraintValueAsBoolean(constraints_, kEchoCancellation,
136 &value_bool) && !value_bool)) {
137 default_audio_processing_constraint_value_ = false;
141 MediaAudioConstraints::~MediaAudioConstraints() {}
143 bool MediaAudioConstraints::GetProperty(const std::string& key) const {
144 // Return the value if the constraint is specified in |constraints|,
145 // otherwise return the default value.
146 bool value = false;
147 if (!GetConstraintValueAsBoolean(constraints_, key, &value))
148 value = GetDefaultValueForConstraint(constraints_, key);
150 return value;
153 std::string MediaAudioConstraints::GetPropertyAsString(
154 const std::string& key) const {
155 std::string value;
156 GetConstraintValueAsString(constraints_, key, &value);
157 return value;
160 bool MediaAudioConstraints::GetEchoCancellationProperty() const {
161 // If platform echo canceller is enabled, disable the software AEC.
162 if (effects_ & media::AudioParameters::ECHO_CANCELLER)
163 return false;
165 // If |kEchoCancellation| is specified in the constraints, it will
166 // override the value of |kGoogEchoCancellation|.
167 bool value = false;
168 if (GetConstraintValueAsBoolean(constraints_, kEchoCancellation, &value))
169 return value;
171 return GetProperty(kGoogEchoCancellation);
174 bool MediaAudioConstraints::IsValid() const {
175 blink::WebVector<blink::WebMediaConstraint> mandatory;
176 constraints_.getMandatoryConstraints(mandatory);
177 for (size_t i = 0; i < mandatory.size(); ++i) {
178 const std::string key = mandatory[i].m_name.utf8();
179 if (key == kMediaStreamSource || key == kMediaStreamSourceId ||
180 key == MediaStreamSource::kSourceId) {
181 // Ignore Chrome specific Tab capture and |kSourceId| constraints.
182 continue;
185 bool valid = false;
186 for (size_t j = 0; j < arraysize(kDefaultAudioConstraints); ++j) {
187 if (key == kDefaultAudioConstraints[j].key) {
188 bool value = false;
189 valid = GetMandatoryConstraintValueAsBoolean(constraints_, key, &value);
190 break;
194 if (!valid) {
195 DLOG(ERROR) << "Invalid MediaStream constraint. Name: " << key;
196 return false;
200 return true;
203 bool MediaAudioConstraints::GetDefaultValueForConstraint(
204 const blink::WebMediaConstraints& constraints,
205 const std::string& key) const {
206 if (!default_audio_processing_constraint_value_)
207 return false;
209 for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) {
210 if (kDefaultAudioConstraints[i].key == key)
211 return kDefaultAudioConstraints[i].value;
214 return false;
217 EchoInformation::EchoInformation()
218 : num_chunks_(0), echo_frames_received_(false) {
221 EchoInformation::~EchoInformation() {}
223 void EchoInformation::UpdateAecDelayStats(
224 webrtc::EchoCancellation* echo_cancellation) {
225 // Only start collecting stats if we know echo cancellation has measured an
226 // echo. Otherwise we clutter the stats with for example cases where only the
227 // microphone is used.
228 if (!echo_frames_received_ & !echo_cancellation->stream_has_echo())
229 return;
231 echo_frames_received_ = true;
232 // In WebRTC, three echo delay metrics are calculated and updated every
233 // five seconds. We use one of them, |fraction_poor_delays| to log in a UMA
234 // histogram an Echo Cancellation quality metric. The stat in WebRTC has a
235 // fixed aggregation window of five seconds, so we use the same query
236 // frequency to avoid logging old values.
237 const int kNumChunksInFiveSeconds = 500;
238 if (!echo_cancellation->is_delay_logging_enabled() ||
239 !echo_cancellation->is_enabled()) {
240 return;
243 num_chunks_++;
244 if (num_chunks_ < kNumChunksInFiveSeconds) {
245 return;
248 int dummy_median = 0, dummy_std = 0;
249 float fraction_poor_delays = 0;
250 if (echo_cancellation->GetDelayMetrics(
251 &dummy_median, &dummy_std, &fraction_poor_delays) ==
252 webrtc::AudioProcessing::kNoError) {
253 num_chunks_ = 0;
254 // Map |fraction_poor_delays| to an Echo Cancellation quality and log in UMA
255 // histogram. See DelayBasedEchoQuality for information on histogram
256 // buckets.
257 UMA_HISTOGRAM_ENUMERATION("WebRTC.AecDelayBasedQuality",
258 EchoDelayFrequencyToQuality(fraction_poor_delays),
259 DELAY_BASED_ECHO_QUALITY_MAX);
263 void EnableEchoCancellation(AudioProcessing* audio_processing) {
264 #if defined(OS_ANDROID) || defined(OS_IOS)
265 const std::string group_name =
266 base::FieldTrialList::FindFullName("ReplaceAECMWithAEC");
267 if (group_name.empty() ||
268 !(group_name == "Enabled" || group_name == "DefaultEnabled")) {
269 // Mobile devices are using AECM.
270 int err = audio_processing->echo_control_mobile()->set_routing_mode(
271 webrtc::EchoControlMobile::kSpeakerphone);
272 err |= audio_processing->echo_control_mobile()->Enable(true);
273 CHECK_EQ(err, 0);
274 return;
276 #endif
277 int err = audio_processing->echo_cancellation()->set_suppression_level(
278 webrtc::EchoCancellation::kHighSuppression);
280 // Enable the metrics for AEC.
281 err |= audio_processing->echo_cancellation()->enable_metrics(true);
282 err |= audio_processing->echo_cancellation()->enable_delay_logging(true);
283 err |= audio_processing->echo_cancellation()->Enable(true);
284 CHECK_EQ(err, 0);
287 void EnableNoiseSuppression(AudioProcessing* audio_processing,
288 webrtc::NoiseSuppression::Level ns_level) {
289 int err = audio_processing->noise_suppression()->set_level(ns_level);
290 err |= audio_processing->noise_suppression()->Enable(true);
291 CHECK_EQ(err, 0);
294 void EnableHighPassFilter(AudioProcessing* audio_processing) {
295 CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0);
298 void EnableTypingDetection(AudioProcessing* audio_processing,
299 webrtc::TypingDetection* typing_detector) {
300 int err = audio_processing->voice_detection()->Enable(true);
301 err |= audio_processing->voice_detection()->set_likelihood(
302 webrtc::VoiceDetection::kVeryLowLikelihood);
303 CHECK_EQ(err, 0);
305 // Configure the update period to 1s (100 * 10ms) in the typing detector.
306 typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
309 void StartEchoCancellationDump(AudioProcessing* audio_processing,
310 base::File aec_dump_file) {
311 DCHECK(aec_dump_file.IsValid());
313 FILE* stream = base::FileToFILE(aec_dump_file.Pass(), "w");
314 if (!stream) {
315 LOG(ERROR) << "Failed to open AEC dump file";
316 return;
319 if (audio_processing->StartDebugRecording(stream))
320 DLOG(ERROR) << "Fail to start AEC debug recording";
323 void StopEchoCancellationDump(AudioProcessing* audio_processing) {
324 if (audio_processing->StopDebugRecording())
325 DLOG(ERROR) << "Fail to stop AEC debug recording";
328 void EnableAutomaticGainControl(AudioProcessing* audio_processing) {
329 #if defined(OS_ANDROID) || defined(OS_IOS)
330 const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital;
331 #else
332 const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog;
333 #endif
334 int err = audio_processing->gain_control()->set_mode(mode);
335 err |= audio_processing->gain_control()->Enable(true);
336 CHECK_EQ(err, 0);
339 void GetAecStats(webrtc::EchoCancellation* echo_cancellation,
340 webrtc::AudioProcessorInterface::AudioProcessorStats* stats) {
341 // These values can take on valid negative values, so use the lowest possible
342 // level as default rather than -1.
343 stats->echo_return_loss = -100;
344 stats->echo_return_loss_enhancement = -100;
346 // The median value can also be negative, but in practice -1 is only used to
347 // signal insufficient data, since the resolution is limited to multiples
348 // of 4ms.
349 stats->echo_delay_median_ms = -1;
350 stats->echo_delay_std_ms = -1;
352 // TODO(ajm): Re-enable this metric once we have a reliable implementation.
353 stats->aec_quality_min = -1.0f;
355 if (!echo_cancellation->are_metrics_enabled() ||
356 !echo_cancellation->is_delay_logging_enabled() ||
357 !echo_cancellation->is_enabled()) {
358 return;
361 // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary
362 // here, but it appears to be unsuitable currently. Revisit after this is
363 // investigated: http://b/issue?id=5666755
364 webrtc::EchoCancellation::Metrics echo_metrics;
365 if (!echo_cancellation->GetMetrics(&echo_metrics)) {
366 stats->echo_return_loss = echo_metrics.echo_return_loss.instant;
367 stats->echo_return_loss_enhancement =
368 echo_metrics.echo_return_loss_enhancement.instant;
371 int median = 0, std = 0;
372 float dummy = 0;
373 if (echo_cancellation->GetDelayMetrics(&median, &std, &dummy) ==
374 webrtc::AudioProcessing::kNoError) {
375 stats->echo_delay_median_ms = median;
376 stats->echo_delay_std_ms = std;
380 CONTENT_EXPORT std::vector<webrtc::Point> ParseArrayGeometry(
381 const std::string& geometry_string) {
382 const auto& tokens =
383 base::SplitString(geometry_string, base::kWhitespaceASCII,
384 base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
385 std::vector<webrtc::Point> geometry;
386 if (tokens.size() < 3 || tokens.size() % 3 != 0) {
387 LOG(ERROR) << "Malformed geometry string: " << geometry_string;
388 return geometry;
391 std::vector<float> float_tokens;
392 float_tokens.reserve(tokens.size());
393 for (const auto& token : tokens) {
394 double float_token;
395 if (!base::StringToDouble(token, &float_token)) {
396 LOG(ERROR) << "Unable to convert token=" << token
397 << " to double from geometry string: " << geometry_string;
398 return geometry;
400 float_tokens.push_back(float_token);
403 geometry.reserve(float_tokens.size() / 3);
404 for (size_t i = 0; i < float_tokens.size(); i += 3) {
405 geometry.push_back(webrtc::Point(float_tokens[i + 0], float_tokens[i + 1],
406 float_tokens[i + 2]));
409 return geometry;
412 } // namespace content