Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / media_stream_audio_processor_options.cc
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.
4
5 #include "content/renderer/media/media_stream_audio_processor_options.h"
6
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"
22
23 namespace content {
24
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";
42
43 namespace {
44
45 // Constant constraint keys which enables default audio constraints on
46 // mediastreams with audio.
47 struct {
48   const char* key;
49   bool value;
50 } const kDefaultAudioConstraints[] = {
51   { MediaAudioConstraints::kEchoCancellation, true },
52   { MediaAudioConstraints::kGoogEchoCancellation, true },
53 #if defined(OS_ANDROID) || defined(OS_IOS)
54   { MediaAudioConstraints::kGoogExperimentalEchoCancellation, false },
55 #else
56   // Enable the extended filter mode AEC on all non-mobile platforms.
57   { MediaAudioConstraints::kGoogExperimentalEchoCancellation, true },
58 #endif
59   { MediaAudioConstraints::kGoogAutoGainControl, true },
60   { MediaAudioConstraints::kGoogExperimentalAutoGainControl, true },
61   { MediaAudioConstraints::kGoogNoiseSuppression, true },
62   { MediaAudioConstraints::kGoogHighpassFilter, true },
63   { MediaAudioConstraints::kGoogTypingNoiseDetection, true },
64   { MediaAudioConstraints::kGoogExperimentalNoiseSuppression, false },
65 #if defined(OS_WIN)
66   { kMediaStreamAudioDucking, true },
67 #else
68   { kMediaStreamAudioDucking, false },
69 #endif
70 };
71
72 bool IsAudioProcessingConstraint(const std::string& key) {
73   // |kMediaStreamAudioDucking| does not require audio processing.
74   return key != kMediaStreamAudioDucking;
75 }
76
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
83 };
84
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;
98   else
99     return DELAY_BASED_ECHO_QUALITY_BAD;
100 }
101
102 }  // namespace
103
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;
118     }
119   }
120 }
121
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;
138   }
139 }
140
141 MediaAudioConstraints::~MediaAudioConstraints() {}
142
143 // TODO(xians): Remove this method after the APM in WebRtc is deprecated.
144 bool MediaAudioConstraints::NeedsAudioProcessing() {
145   if (GetEchoCancellationProperty())
146     return true;
147
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)) {
155       return true;
156     }
157   }
158
159   return false;
160 }
161
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.
165   bool value = false;
166   if (!GetConstraintValueAsBoolean(constraints_, key, &value))
167     value = GetDefaultValueForConstraint(constraints_, key);
168
169   return value;
170 }
171
172 bool MediaAudioConstraints::GetEchoCancellationProperty() {
173   // If platform echo canceller is enabled, disable the software AEC.
174   if (effects_ & media::AudioParameters::ECHO_CANCELLER)
175     return false;
176
177   // If |kEchoCancellation| is specified in the constraints, it will
178   // override the value of |kGoogEchoCancellation|.
179   bool value = false;
180   if (GetConstraintValueAsBoolean(constraints_, kEchoCancellation, &value))
181     return value;
182
183   return GetProperty(kGoogEchoCancellation);
184 }
185
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.
194       continue;
195     }
196
197     bool valid = false;
198     for (size_t j = 0; j < arraysize(kDefaultAudioConstraints); ++j) {
199       if (key == kDefaultAudioConstraints[j].key) {
200         bool value = false;
201         valid = GetMandatoryConstraintValueAsBoolean(constraints_, key, &value);
202         break;
203       }
204     }
205
206     if (!valid) {
207       DLOG(ERROR) << "Invalid MediaStream constraint. Name: " << key;
208       return false;
209     }
210   }
211
212   return true;
213 }
214
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
219   // audio processing.
220   if (!default_audio_processing_constraint_value_ &&
221       IsAudioProcessingConstraint(key))
222     return false;
223
224   for (size_t i = 0; i < arraysize(kDefaultAudioConstraints); ++i) {
225     if (kDefaultAudioConstraints[i].key == key)
226       return kDefaultAudioConstraints[i].value;
227   }
228
229   return false;
230 }
231
232 EchoInformation::EchoInformation()
233     : echo_poor_delay_counts_(0),
234       echo_total_delay_counts_(0),
235       last_log_time_(base::TimeTicks::Now()) {}
236
237 EchoInformation::~EchoInformation() {}
238
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_;
252   }
253   ++echo_total_delay_counts_;
254   LogAecDelayStats();
255 }
256
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)
263     return;
264
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);
275   }
276   echo_poor_delay_counts_ = 0;
277   echo_total_delay_counts_ = 0;
278   last_log_time_ = base::TimeTicks::Now();
279 }
280
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);
290     CHECK_EQ(err, 0);
291     return;
292   }
293 #endif
294   int err = audio_processing->echo_cancellation()->set_suppression_level(
295       webrtc::EchoCancellation::kHighSuppression);
296
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);
301   CHECK_EQ(err, 0);
302 }
303
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);
308   CHECK_EQ(err, 0);
309 }
310
311 void EnableHighPassFilter(AudioProcessing* audio_processing) {
312   CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0);
313 }
314
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);
320   CHECK_EQ(err, 0);
321
322   // Configure the update period to 1s (100 * 10ms) in the typing detector.
323   typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
324 }
325
326 void StartEchoCancellationDump(AudioProcessing* audio_processing,
327                                base::File aec_dump_file) {
328   DCHECK(aec_dump_file.IsValid());
329
330   FILE* stream = base::FileToFILE(aec_dump_file.Pass(), "w");
331   if (!stream) {
332     LOG(ERROR) << "Failed to open AEC dump file";
333     return;
334   }
335
336   if (audio_processing->StartDebugRecording(stream))
337     DLOG(ERROR) << "Fail to start AEC debug recording";
338 }
339
340 void StopEchoCancellationDump(AudioProcessing* audio_processing) {
341   if (audio_processing->StopDebugRecording())
342     DLOG(ERROR) << "Fail to stop AEC debug recording";
343 }
344
345 void EnableAutomaticGainControl(AudioProcessing* audio_processing) {
346 #if defined(OS_ANDROID) || defined(OS_IOS)
347   const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital;
348 #else
349   const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog;
350 #endif
351   int err = audio_processing->gain_control()->set_mode(mode);
352   err |= audio_processing->gain_control()->Enable(true);
353   CHECK_EQ(err, 0);
354 }
355
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;
362
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
365   // of 4ms.
366   stats->echo_delay_median_ms = -1;
367   stats->echo_delay_std_ms = -1;
368
369   // TODO(ajm): Re-enable this metric once we have a reliable implementation.
370   stats->aec_quality_min = -1.0f;
371
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()) {
375     return;
376   }
377
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;
386   }
387
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;
392   }
393 }
394
395 }  // namespace content