Home | History | Annotate | Download | only in media
      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/file_util.h"
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #include "base/metrics/field_trial.h"
     11 #include "base/path_service.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "content/common/media/media_stream_options.h"
     15 #include "content/renderer/media/media_stream_constraints_util.h"
     16 #include "content/renderer/media/media_stream_source.h"
     17 #include "content/renderer/media/rtc_media_constraints.h"
     18 #include "media/audio/audio_parameters.h"
     19 #include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
     20 #include "third_party/webrtc/modules/audio_processing/typing_detection.h"
     21 
     22 namespace content {
     23 
     24 const char MediaAudioConstraints::kEchoCancellation[] = "echoCancellation";
     25 const char MediaAudioConstraints::kGoogEchoCancellation[] =
     26     "googEchoCancellation";
     27 const char MediaAudioConstraints::kGoogExperimentalEchoCancellation[] =
     28     "googEchoCancellation2";
     29 const char MediaAudioConstraints::kGoogAutoGainControl[] =
     30     "googAutoGainControl";
     31 const char MediaAudioConstraints::kGoogExperimentalAutoGainControl[] =
     32     "googAutoGainControl2";
     33 const char MediaAudioConstraints::kGoogNoiseSuppression[] =
     34     "googNoiseSuppression";
     35 const char MediaAudioConstraints::kGoogExperimentalNoiseSuppression[] =
     36     "googNoiseSuppression2";
     37 const char MediaAudioConstraints::kGoogHighpassFilter[] = "googHighpassFilter";
     38 const char MediaAudioConstraints::kGoogTypingNoiseDetection[] =
     39     "googTypingNoiseDetection";
     40 const char MediaAudioConstraints::kGoogAudioMirroring[] = "googAudioMirroring";
     41 
     42 namespace {
     43 
     44 // Constant constraint keys which enables default audio constraints on
     45 // mediastreams with audio.
     46 struct {
     47   const char* key;
     48   bool value;
     49 } const kDefaultAudioConstraints[] = {
     50   { MediaAudioConstraints::kEchoCancellation, true },
     51   { MediaAudioConstraints::kGoogEchoCancellation, true },
     52 #if defined(OS_CHROMEOS) || defined(OS_MACOSX)
     53   // Enable the extended filter mode AEC on platforms with known echo issues.
     54   { MediaAudioConstraints::kGoogExperimentalEchoCancellation, true },
     55 #else
     56   { MediaAudioConstraints::kGoogExperimentalEchoCancellation, false },
     57 #endif
     58   { MediaAudioConstraints::kGoogAutoGainControl, true },
     59   { MediaAudioConstraints::kGoogExperimentalAutoGainControl, true },
     60   { MediaAudioConstraints::kGoogNoiseSuppression, true },
     61   { MediaAudioConstraints::kGoogHighpassFilter, true },
     62   { MediaAudioConstraints::kGoogTypingNoiseDetection, true },
     63   { MediaAudioConstraints::kGoogExperimentalNoiseSuppression, false },
     64 #if defined(OS_WIN)
     65   { kMediaStreamAudioDucking, true },
     66 #else
     67   { kMediaStreamAudioDucking, false },
     68 #endif
     69 };
     70 
     71 bool IsAudioProcessingConstraint(const std::string& key) {
     72   // |kMediaStreamAudioDucking| does not require audio processing.
     73   return key != kMediaStreamAudioDucking;
     74 }
     75 
     76 } // namespace
     77 
     78 // TODO(xians): Remove this method after the APM in WebRtc is deprecated.
     79 void MediaAudioConstraints::ApplyFixedAudioConstraints(
     80     RTCMediaConstraints* constraints) {
     81   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
     82     bool already_set_value;
     83     if (!webrtc::FindConstraint(constraints, kDefaultAudioConstraints[i].key,
     84                                 &already_set_value, NULL)) {
     85       const std::string value = kDefaultAudioConstraints[i].value ?
     86           webrtc::MediaConstraintsInterface::kValueTrue :
     87           webrtc::MediaConstraintsInterface::kValueFalse;
     88       constraints->AddOptional(kDefaultAudioConstraints[i].key, value, false);
     89     } else {
     90       DVLOG(1) << "Constraint " << kDefaultAudioConstraints[i].key
     91                << " already set to " << already_set_value;
     92     }
     93   }
     94 }
     95 
     96 MediaAudioConstraints::MediaAudioConstraints(
     97     const blink::WebMediaConstraints& constraints, int effects)
     98     : constraints_(constraints),
     99       effects_(effects),
    100       default_audio_processing_constraint_value_(true) {
    101   // The default audio processing constraints are turned off when
    102   // - gUM has a specific kMediaStreamSource, which is used by tab capture
    103   //   and screen capture.
    104   // - |kEchoCancellation| is explicitly set to false.
    105   std::string value_str;
    106   bool value_bool = false;
    107   if ((GetConstraintValueAsString(constraints, kMediaStreamSource,
    108                                   &value_str)) ||
    109       (GetConstraintValueAsBoolean(constraints_, kEchoCancellation,
    110                                    &value_bool) && !value_bool)) {
    111     default_audio_processing_constraint_value_ = false;
    112   }
    113 }
    114 
    115 MediaAudioConstraints::~MediaAudioConstraints() {}
    116 
    117 // TODO(xians): Remove this method after the APM in WebRtc is deprecated.
    118 bool MediaAudioConstraints::NeedsAudioProcessing() {
    119   if (GetEchoCancellationProperty())
    120     return true;
    121 
    122   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    123     // |kEchoCancellation| and |kGoogEchoCancellation| have been convered by
    124     // GetEchoCancellationProperty().
    125     if (kDefaultAudioConstraints[i].key != kEchoCancellation &&
    126         kDefaultAudioConstraints[i].key != kGoogEchoCancellation &&
    127         IsAudioProcessingConstraint(kDefaultAudioConstraints[i].key) &&
    128         GetProperty(kDefaultAudioConstraints[i].key)) {
    129       return true;
    130     }
    131   }
    132 
    133   return false;
    134 }
    135 
    136 bool MediaAudioConstraints::GetProperty(const std::string& key) {
    137   // Return the value if the constraint is specified in |constraints|,
    138   // otherwise return the default value.
    139   bool value = false;
    140   if (!GetConstraintValueAsBoolean(constraints_, key, &value))
    141     value = GetDefaultValueForConstraint(constraints_, key);
    142 
    143   return value;
    144 }
    145 
    146 bool MediaAudioConstraints::GetEchoCancellationProperty() {
    147   // If platform echo canceller is enabled, disable the software AEC.
    148   if (effects_ & media::AudioParameters::ECHO_CANCELLER)
    149     return false;
    150 
    151   // If |kEchoCancellation| is specified in the constraints, it will
    152   // override the value of |kGoogEchoCancellation|.
    153   bool value = false;
    154   if (GetConstraintValueAsBoolean(constraints_, kEchoCancellation, &value))
    155     return value;
    156 
    157   return GetProperty(kGoogEchoCancellation);
    158 }
    159 
    160 bool MediaAudioConstraints::IsValid() {
    161   blink::WebVector<blink::WebMediaConstraint> mandatory;
    162   constraints_.getMandatoryConstraints(mandatory);
    163   for (size_t i = 0; i < mandatory.size(); ++i) {
    164     const std::string key = mandatory[i].m_name.utf8();
    165     if (key == kMediaStreamSource || key == kMediaStreamSourceId ||
    166         key == MediaStreamSource::kSourceId) {
    167       // Ignore Chrome specific Tab capture and |kSourceId| constraints.
    168       continue;
    169     }
    170 
    171     bool valid = false;
    172     for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++j) {
    173       if (key == kDefaultAudioConstraints[j].key) {
    174         bool value = false;
    175         valid = GetMandatoryConstraintValueAsBoolean(constraints_, key, &value);
    176         break;
    177       }
    178     }
    179 
    180     if (!valid) {
    181       DLOG(ERROR) << "Invalid MediaStream constraint. Name: " << key;
    182       return false;
    183     }
    184   }
    185 
    186   return true;
    187 }
    188 
    189 bool MediaAudioConstraints::GetDefaultValueForConstraint(
    190     const blink::WebMediaConstraints& constraints, const std::string& key) {
    191   // |kMediaStreamAudioDucking| is not restricted by
    192   // |default_audio_processing_constraint_value_| since it does not require
    193   // audio processing.
    194   if (!default_audio_processing_constraint_value_ &&
    195       IsAudioProcessingConstraint(key))
    196     return false;
    197 
    198   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kDefaultAudioConstraints); ++i) {
    199     if (kDefaultAudioConstraints[i].key == key)
    200       return kDefaultAudioConstraints[i].value;
    201   }
    202 
    203   return false;
    204 }
    205 
    206 void EnableEchoCancellation(AudioProcessing* audio_processing) {
    207 #if defined(OS_ANDROID) || defined(OS_IOS)
    208   const std::string group_name =
    209       base::FieldTrialList::FindFullName("ReplaceAECMWithAEC");
    210   if (group_name.empty() || group_name != "Enabled") {
    211     // Mobile devices are using AECM.
    212     int err = audio_processing->echo_control_mobile()->set_routing_mode(
    213         webrtc::EchoControlMobile::kSpeakerphone);
    214     err |= audio_processing->echo_control_mobile()->Enable(true);
    215     CHECK_EQ(err, 0);
    216     return;
    217   }
    218 #endif
    219   int err = audio_processing->echo_cancellation()->set_suppression_level(
    220       webrtc::EchoCancellation::kHighSuppression);
    221 
    222   // Enable the metrics for AEC.
    223   err |= audio_processing->echo_cancellation()->enable_metrics(true);
    224   err |= audio_processing->echo_cancellation()->enable_delay_logging(true);
    225   err |= audio_processing->echo_cancellation()->Enable(true);
    226   CHECK_EQ(err, 0);
    227 }
    228 
    229 void EnableNoiseSuppression(AudioProcessing* audio_processing) {
    230   int err = audio_processing->noise_suppression()->set_level(
    231       webrtc::NoiseSuppression::kHigh);
    232   err |= audio_processing->noise_suppression()->Enable(true);
    233   CHECK_EQ(err, 0);
    234 }
    235 
    236 void EnableExperimentalNoiseSuppression(AudioProcessing* audio_processing) {
    237   CHECK_EQ(audio_processing->EnableExperimentalNs(true), 0);
    238 }
    239 
    240 void EnableHighPassFilter(AudioProcessing* audio_processing) {
    241   CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0);
    242 }
    243 
    244 void EnableTypingDetection(AudioProcessing* audio_processing,
    245                            webrtc::TypingDetection* typing_detector) {
    246   int err = audio_processing->voice_detection()->Enable(true);
    247   err |= audio_processing->voice_detection()->set_likelihood(
    248       webrtc::VoiceDetection::kVeryLowLikelihood);
    249   CHECK_EQ(err, 0);
    250 
    251   // Configure the update period to 1s (100 * 10ms) in the typing detector.
    252   typing_detector->SetParameters(0, 0, 0, 0, 0, 100);
    253 }
    254 
    255 void EnableExperimentalEchoCancellation(AudioProcessing* audio_processing) {
    256   webrtc::Config config;
    257   config.Set<webrtc::DelayCorrection>(new webrtc::DelayCorrection(true));
    258   audio_processing->SetExtraOptions(config);
    259 }
    260 
    261 void StartEchoCancellationDump(AudioProcessing* audio_processing,
    262                                base::File aec_dump_file) {
    263   DCHECK(aec_dump_file.IsValid());
    264 
    265   FILE* stream = base::FileToFILE(aec_dump_file.Pass(), "w");
    266   if (!stream) {
    267     LOG(ERROR) << "Failed to open AEC dump file";
    268     return;
    269   }
    270 
    271   if (audio_processing->StartDebugRecording(stream))
    272     DLOG(ERROR) << "Fail to start AEC debug recording";
    273 }
    274 
    275 void StopEchoCancellationDump(AudioProcessing* audio_processing) {
    276   if (audio_processing->StopDebugRecording())
    277     DLOG(ERROR) << "Fail to stop AEC debug recording";
    278 }
    279 
    280 void EnableAutomaticGainControl(AudioProcessing* audio_processing) {
    281 #if defined(OS_ANDROID) || defined(OS_IOS)
    282   const webrtc::GainControl::Mode mode = webrtc::GainControl::kFixedDigital;
    283 #else
    284   const webrtc::GainControl::Mode mode = webrtc::GainControl::kAdaptiveAnalog;
    285 #endif
    286   int err = audio_processing->gain_control()->set_mode(mode);
    287   err |= audio_processing->gain_control()->Enable(true);
    288   CHECK_EQ(err, 0);
    289 }
    290 
    291 void GetAecStats(AudioProcessing* audio_processing,
    292                  webrtc::AudioProcessorInterface::AudioProcessorStats* stats) {
    293   // These values can take on valid negative values, so use the lowest possible
    294   // level as default rather than -1.
    295   stats->echo_return_loss = -100;
    296   stats->echo_return_loss_enhancement = -100;
    297 
    298   // These values can also be negative, but in practice -1 is only used to
    299   // signal insufficient data, since the resolution is limited to multiples
    300   // of 4ms.
    301   stats->echo_delay_median_ms = -1;
    302   stats->echo_delay_std_ms = -1;
    303 
    304   // TODO(ajm): Re-enable this metric once we have a reliable implementation.
    305   stats->aec_quality_min = -1.0f;
    306 
    307   if (!audio_processing->echo_cancellation()->are_metrics_enabled() ||
    308       !audio_processing->echo_cancellation()->is_delay_logging_enabled() ||
    309       !audio_processing->echo_cancellation()->is_enabled()) {
    310     return;
    311   }
    312 
    313   // TODO(ajm): we may want to use VoECallReport::GetEchoMetricsSummary
    314   // here, but it appears to be unsuitable currently. Revisit after this is
    315   // investigated: http://b/issue?id=5666755
    316   webrtc::EchoCancellation::Metrics echo_metrics;
    317   if (!audio_processing->echo_cancellation()->GetMetrics(&echo_metrics)) {
    318     stats->echo_return_loss = echo_metrics.echo_return_loss.instant;
    319     stats->echo_return_loss_enhancement =
    320         echo_metrics.echo_return_loss_enhancement.instant;
    321   }
    322 
    323   int median = 0, std = 0;
    324   if (!audio_processing->echo_cancellation()->GetDelayMetrics(&median, &std)) {
    325     stats->echo_delay_median_ms = median;
    326     stats->echo_delay_std_ms = std;
    327   }
    328 }
    329 
    330 }  // namespace content
    331