| Index: content/renderer/media/media_stream_constraints_util_audio.cc
|
| diff --git a/content/renderer/media/media_stream_constraints_util_audio.cc b/content/renderer/media/media_stream_constraints_util_audio.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..155d70698f2d68525e728362a9c7dd848b71b8f6
|
| --- /dev/null
|
| +++ b/content/renderer/media/media_stream_constraints_util_audio.cc
|
| @@ -0,0 +1,614 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "content/renderer/media/media_stream_constraints_util_audio.h"
|
| +
|
| +#include <algorithm>
|
| +#include <cmath>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "content/common/media/media_stream_options.h"
|
| +#include "content/renderer/media/media_stream_constraints_util_sets.h"
|
| +#include "content/renderer/media/media_stream_video_source.h"
|
| +#include "media/base/limits.h"
|
| +#include "third_party/WebKit/public/platform/WebMediaConstraints.h"
|
| +#include "third_party/WebKit/public/platform/WebString.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +// This class has the same data as ::mojom::AudioInputDeviceCapabilities, but
|
| +// adds extra operations to simplify the device-selection code.
|
| +class AudioDeviceInfo {
|
| + public:
|
| + // This constructor is intended for device capture.
|
| + explicit AudioDeviceInfo(
|
| + const ::mojom::AudioInputDeviceCapabilitiesPtr& device_info)
|
| + : device_id_(device_info->device_id),
|
| + parameters_(device_info->parameters) {
|
| + DCHECK(parameters_.IsValid());
|
| + }
|
| +
|
| + // This constructor is intended for content capture, where constraints on
|
| + // audio parameters are not supported.
|
| + explicit AudioDeviceInfo(std::string device_id)
|
| + : device_id_(std::move(device_id)) {}
|
| +
|
| + bool operator==(const AudioDeviceInfo& other) const {
|
| + return device_id_ == other.device_id_;
|
| + }
|
| +
|
| + // Accessors
|
| + const std::string& device_id() const { return device_id_; }
|
| + const media::AudioParameters& parameters() const { return parameters_; }
|
| +
|
| + // Convenience accessors
|
| + int SampleRate() const {
|
| + DCHECK(parameters_.IsValid());
|
| + return parameters_.sample_rate();
|
| + }
|
| + int SampleSize() const {
|
| + DCHECK(parameters_.IsValid());
|
| + return parameters_.bits_per_sample();
|
| + }
|
| + int ChannelCount() const {
|
| + DCHECK(parameters_.IsValid());
|
| + return parameters_.channels();
|
| + }
|
| + int Effects() const {
|
| + DCHECK(parameters_.IsValid());
|
| + return parameters_.effects();
|
| + }
|
| +
|
| + private:
|
| + std::string device_id_;
|
| + media::AudioParameters parameters_;
|
| +};
|
| +
|
| +using AudioDeviceSet = DiscreteSet<AudioDeviceInfo>;
|
| +
|
| +AudioDeviceSet AudioDeviceSetForDeviceCapture(
|
| + const blink::WebMediaTrackConstraintSet& constraint_set,
|
| + const AudioDeviceCaptureCapabilities& capabilities,
|
| + const char** failed_constraint_name) {
|
| + std::vector<AudioDeviceInfo> result;
|
| + *failed_constraint_name = "";
|
| + for (auto& device_capabilities : capabilities) {
|
| + if (!constraint_set.device_id.Matches(
|
| + blink::WebString::FromASCII(device_capabilities->device_id))) {
|
| + if (failed_constraint_name)
|
| + *failed_constraint_name = constraint_set.device_id.GetName();
|
| + continue;
|
| + }
|
| + *failed_constraint_name =
|
| + IsOutsideConstraintRange(constraint_set.sample_rate,
|
| + device_capabilities->parameters.sample_rate());
|
| + if (*failed_constraint_name)
|
| + continue;
|
| +
|
| + *failed_constraint_name = IsOutsideConstraintRange(
|
| + constraint_set.sample_size,
|
| + device_capabilities->parameters.bits_per_sample());
|
| + if (*failed_constraint_name)
|
| + continue;
|
| +
|
| + *failed_constraint_name =
|
| + IsOutsideConstraintRange(constraint_set.channel_count,
|
| + device_capabilities->parameters.channels());
|
| + if (*failed_constraint_name)
|
| + continue;
|
| +
|
| + result.push_back(AudioDeviceInfo(device_capabilities));
|
| + }
|
| +
|
| + if (!result.empty())
|
| + *failed_constraint_name = nullptr;
|
| +
|
| + return AudioDeviceSet(result);
|
| +}
|
| +
|
| +AudioDeviceSet AudioDeviceSetForContentCapture(
|
| + const blink::WebMediaTrackConstraintSet& constraint_set,
|
| + const char** failed_constraint_name = nullptr) {
|
| + if (!constraint_set.device_id.HasExact())
|
| + return AudioDeviceSet::UniversalSet();
|
| +
|
| + std::vector<AudioDeviceInfo> result;
|
| + for (auto& device_id : constraint_set.device_id.Exact())
|
| + result.push_back(AudioDeviceInfo(device_id.Utf8()));
|
| +
|
| + return AudioDeviceSet(result);
|
| +}
|
| +
|
| +// This class represents a set of possible candidate settings.
|
| +// The SelectSettings algorithm starts with a set containing all possible
|
| +// candidates based on hardware capabilities and/or allowed values for supported
|
| +// properties. The is then reduced progressively as the basic and advanced
|
| +// constraint sets are applied.
|
| +// In the end, if the set of candidates is empty, SelectSettings fails.
|
| +// If not, the ideal values (if any) or tie breaker rules are used to select
|
| +// the final settings based on the candidates that survived the application
|
| +// of the constraint sets.
|
| +// This class is implemented as a collection of more specific sets for the
|
| +// various supported properties. If any of the specific sets is empty, the
|
| +// whole AudioCaptureCandidates set is considered empty as well.
|
| +class AudioCaptureCandidates {
|
| + public:
|
| + enum BoolConstraint {
|
| + // Constraints not related to audio processing.
|
| + HOTWORD_ENABLED,
|
| + DISABLE_LOCAL_ECHO,
|
| + RENDER_TO_ASSOCIATED_SINK,
|
| +
|
| + // Constraints that enable/disable audio processing.
|
| + ECHO_CANCELLATION,
|
| + GOOG_ECHO_CANCELLATION,
|
| +
|
| + // Constraints that control audio-processing behavior.
|
| + GOOG_AUDIO_MIRRORING,
|
| + GOOG_AUTO_GAIN_CONTROL,
|
| + GOOG_EXPERIMENTAL_ECHO_CANCELLATION,
|
| + GOOG_TYPING_NOISE_DETECTION,
|
| + GOOG_NOISE_SUPPRESSION,
|
| + GOOG_EXPERIMENTAL_NOISE_SUPPRESSION,
|
| + GOOG_BEAMFORMING,
|
| + GOOG_HIGHPASS_FILTER,
|
| + GOOG_EXPERIMENTAL_AUTO_GAIN_CONTROL,
|
| + NUM_BOOL_CONSTRAINTS
|
| + };
|
| +
|
| + AudioCaptureCandidates();
|
| +
|
| + AudioCaptureCandidates(
|
| + const blink::WebMediaTrackConstraintSet& constraint_set,
|
| + const AudioDeviceCaptureCapabilities& capabilities,
|
| + bool is_device_capture);
|
| +
|
| + // Set operations.
|
| + bool IsEmpty() const { return failed_constraint_name_ != nullptr; }
|
| + AudioCaptureCandidates Intersection(const AudioCaptureCandidates& other);
|
| +
|
| + // Accessors.
|
| + const char* failed_constraint_name() const { return failed_constraint_name_; }
|
| + const AudioDeviceSet& audio_device_set() const { return audio_device_set_; }
|
| + const DiscreteSet<std::string>& goog_array_geometry_set() const {
|
| + return goog_array_geometry_set_;
|
| + }
|
| +
|
| + // Accessor for boolean sets.
|
| + const DiscreteSet<bool>& GetBoolSet(BoolConstraint property) const {
|
| + DCHECK_GE(property, 0);
|
| + DCHECK_LT(property, NUM_BOOL_CONSTRAINTS);
|
| + return bool_sets_[property];
|
| + }
|
| +
|
| + // Convenience accessors.
|
| + const DiscreteSet<bool>& hotword_enabled_set() const {
|
| + return bool_sets_[HOTWORD_ENABLED];
|
| + }
|
| + const DiscreteSet<bool>& disable_local_echo_set() const {
|
| + return bool_sets_[DISABLE_LOCAL_ECHO];
|
| + }
|
| + const DiscreteSet<bool>& render_to_associated_sink_set() const {
|
| + return bool_sets_[RENDER_TO_ASSOCIATED_SINK];
|
| + }
|
| + const DiscreteSet<bool>& echo_cancellation_set() const {
|
| + return bool_sets_[ECHO_CANCELLATION];
|
| + }
|
| + const DiscreteSet<bool>& goog_echo_cancellation_set() const {
|
| + return bool_sets_[GOOG_ECHO_CANCELLATION];
|
| + }
|
| + const DiscreteSet<bool>& goog_audio_mirroring_set() const {
|
| + return bool_sets_[GOOG_AUDIO_MIRRORING];
|
| + }
|
| +
|
| + private:
|
| + void MaybeUpdateFailedNonDeviceConstraintName();
|
| + void CheckContradictoryEchoCancellation();
|
| +
|
| + // Maps BoolConstraint values to fields in blink::WebMediaTrackConstraintSet.
|
| + static const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*
|
| + kBlinkBoolConstraintFields[NUM_BOOL_CONSTRAINTS];
|
| +
|
| + const char* failed_constraint_name_;
|
| +
|
| + AudioDeviceSet audio_device_set_; // Device-related constraints.
|
| + std::array<DiscreteSet<bool>, NUM_BOOL_CONSTRAINTS> bool_sets_;
|
| + DiscreteSet<std::string> goog_array_geometry_set_;
|
| +};
|
| +
|
| +const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*
|
| + AudioCaptureCandidates::kBlinkBoolConstraintFields[NUM_BOOL_CONSTRAINTS] = {
|
| + &blink::WebMediaTrackConstraintSet::hotword_enabled,
|
| + &blink::WebMediaTrackConstraintSet::disable_local_echo,
|
| + &blink::WebMediaTrackConstraintSet::render_to_associated_sink,
|
| + &blink::WebMediaTrackConstraintSet::echo_cancellation,
|
| + &blink::WebMediaTrackConstraintSet::goog_echo_cancellation,
|
| + &blink::WebMediaTrackConstraintSet::goog_audio_mirroring,
|
| + &blink::WebMediaTrackConstraintSet::goog_auto_gain_control,
|
| + &blink::WebMediaTrackConstraintSet::goog_experimental_echo_cancellation,
|
| + &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection,
|
| + &blink::WebMediaTrackConstraintSet::goog_noise_suppression,
|
| + &blink::WebMediaTrackConstraintSet::goog_experimental_noise_suppression,
|
| + &blink::WebMediaTrackConstraintSet::goog_beamforming,
|
| + &blink::WebMediaTrackConstraintSet::goog_highpass_filter,
|
| + &blink::WebMediaTrackConstraintSet::
|
| + goog_experimental_auto_gain_control};
|
| +
|
| +// directly mapped boolean sets to audio properties
|
| +struct BoolSetPropertyEntry {
|
| + DiscreteSet<bool> AudioCaptureCandidates::*bool_set;
|
| + bool AudioProcessingProperties::*bool_field;
|
| + bool default_value;
|
| +};
|
| +
|
| +AudioCaptureCandidates::AudioCaptureCandidates()
|
| + : failed_constraint_name_(nullptr) {}
|
| +
|
| +AudioCaptureCandidates::AudioCaptureCandidates(
|
| + const blink::WebMediaTrackConstraintSet& constraint_set,
|
| + const AudioDeviceCaptureCapabilities& capabilities,
|
| + bool is_device_capture)
|
| + : failed_constraint_name_(nullptr),
|
| + audio_device_set_(
|
| + is_device_capture
|
| + ? AudioDeviceSetForDeviceCapture(constraint_set,
|
| + capabilities,
|
| + &failed_constraint_name_)
|
| + : AudioDeviceSetForContentCapture(constraint_set,
|
| + &failed_constraint_name_)),
|
| + goog_array_geometry_set_(
|
| + StringSetFromConstraint(constraint_set.goog_array_geometry)) {
|
| + for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
|
| + bool_sets_[i] =
|
| + BoolSetFromConstraint(constraint_set.*kBlinkBoolConstraintFields[i]);
|
| + }
|
| + MaybeUpdateFailedNonDeviceConstraintName();
|
| +}
|
| +
|
| +AudioCaptureCandidates AudioCaptureCandidates::Intersection(
|
| + const AudioCaptureCandidates& other) {
|
| + AudioCaptureCandidates intersection;
|
| + intersection.audio_device_set_ =
|
| + audio_device_set_.Intersection(other.audio_device_set_);
|
| + if (intersection.audio_device_set_.IsEmpty()) {
|
| + // To mark the intersection as empty, it is necessary to assign a
|
| + // a non-null value to |failed_constraint_name_|. The specific value
|
| + // for an intersection does not actually matter, since the intersection
|
| + // is discarded if empty.
|
| + intersection.failed_constraint_name_ = "some device constraint";
|
| + return intersection;
|
| + }
|
| + for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
|
| + intersection.bool_sets_[i] =
|
| + bool_sets_[i].Intersection(other.bool_sets_[i]);
|
| + }
|
| + intersection.goog_array_geometry_set_ =
|
| + goog_array_geometry_set_.Intersection(other.goog_array_geometry_set_);
|
| + intersection.MaybeUpdateFailedNonDeviceConstraintName();
|
| +
|
| + return intersection;
|
| +}
|
| +
|
| +void AudioCaptureCandidates::MaybeUpdateFailedNonDeviceConstraintName() {
|
| + blink::WebMediaTrackConstraintSet constraint_set;
|
| + for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) {
|
| + if (bool_sets_[i].IsEmpty()) {
|
| + failed_constraint_name_ =
|
| + (constraint_set.*kBlinkBoolConstraintFields[i]).GetName();
|
| + }
|
| + }
|
| + if (goog_array_geometry_set_.IsEmpty())
|
| + failed_constraint_name_ = constraint_set.goog_array_geometry.GetName();
|
| +
|
| + CheckContradictoryEchoCancellation();
|
| +}
|
| +
|
| +void AudioCaptureCandidates::CheckContradictoryEchoCancellation() {
|
| + DiscreteSet<bool> echo_cancellation_intersection =
|
| + bool_sets_[ECHO_CANCELLATION].Intersection(
|
| + bool_sets_[GOOG_ECHO_CANCELLATION]);
|
| + // echoCancellation and googEchoCancellation constraints should not
|
| + // contradict each other. Mark the set as empty if they do.
|
| + if (echo_cancellation_intersection.IsEmpty()) {
|
| + failed_constraint_name_ =
|
| + blink::WebMediaTrackConstraintSet().echo_cancellation.GetName();
|
| + }
|
| +}
|
| +
|
| +// Fitness function for constraints involved in device selection.
|
| +// Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance
|
| +double DeviceInfoFitness(
|
| + bool is_device_capture,
|
| + const AudioDeviceInfo& device_info,
|
| + const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
|
| + double fitness = 0.0;
|
| + fitness += StringConstraintFitnessDistance(
|
| + blink::WebString::FromASCII(device_info.device_id()),
|
| + basic_constraint_set.device_id);
|
| +
|
| + if (!is_device_capture)
|
| + return fitness;
|
| +
|
| + if (basic_constraint_set.sample_rate.HasIdeal()) {
|
| + fitness += NumericConstraintFitnessDistance(
|
| + device_info.SampleRate(), basic_constraint_set.sample_rate.Ideal());
|
| + }
|
| +
|
| + if (basic_constraint_set.sample_size.HasIdeal()) {
|
| + fitness += NumericConstraintFitnessDistance(
|
| + device_info.SampleSize(), basic_constraint_set.sample_size.Ideal());
|
| + }
|
| +
|
| + if (basic_constraint_set.channel_count.HasIdeal()) {
|
| + fitness += NumericConstraintFitnessDistance(
|
| + device_info.ChannelCount(), basic_constraint_set.channel_count.Ideal());
|
| + }
|
| +
|
| + return fitness;
|
| +}
|
| +
|
| +AudioDeviceInfo SelectDevice(
|
| + const AudioDeviceSet& audio_device_set,
|
| + const blink::WebMediaTrackConstraintSet& basic_constraint_set,
|
| + const std::string& default_device_id,
|
| + bool is_device_capture) {
|
| + DCHECK(!audio_device_set.IsEmpty());
|
| + if (audio_device_set.is_universal()) {
|
| + DCHECK(!is_device_capture);
|
| + std::string device_id;
|
| + if (basic_constraint_set.device_id.HasIdeal()) {
|
| + device_id = basic_constraint_set.device_id.Ideal().begin()->Utf8();
|
| + }
|
| + return AudioDeviceInfo(std::move(device_id));
|
| + }
|
| +
|
| + std::vector<double> best_fitness({HUGE_VAL, HUGE_VAL});
|
| + auto best_candidate = audio_device_set.elements().end();
|
| + for (auto it = audio_device_set.elements().begin();
|
| + it != audio_device_set.elements().end(); ++it) {
|
| + std::vector<double> fitness;
|
| + // First criterion is spec-based fitness function. Second criterion is
|
| + // being the default device.
|
| + fitness.push_back(
|
| + DeviceInfoFitness(is_device_capture, *it, basic_constraint_set));
|
| + fitness.push_back(it->device_id() == default_device_id ? 0.0 : HUGE_VAL);
|
| + if (fitness < best_fitness) {
|
| + best_fitness = std::move(fitness);
|
| + best_candidate = it;
|
| + }
|
| + }
|
| + DCHECK(best_candidate != audio_device_set.elements().end());
|
| + return *best_candidate;
|
| +}
|
| +
|
| +bool SelectBool(const DiscreteSet<bool>& set,
|
| + const blink::BooleanConstraint& constraint,
|
| + bool default_value) {
|
| + DCHECK(!set.IsEmpty());
|
| + if (constraint.HasIdeal() && set.Contains(constraint.Ideal())) {
|
| + return constraint.Ideal();
|
| + }
|
| +
|
| + // Return default value if unconstrained.
|
| + if (set.is_universal()) {
|
| + return default_value;
|
| + }
|
| + DCHECK_EQ(set.elements().size(), 1U);
|
| + return set.FirstElement();
|
| +}
|
| +
|
| +base::Optional<bool> SelectOptionalBool(
|
| + const DiscreteSet<bool>& set,
|
| + const blink::BooleanConstraint& constraint) {
|
| + DCHECK(!set.IsEmpty());
|
| + if (constraint.HasIdeal() && set.Contains(constraint.Ideal())) {
|
| + return constraint.Ideal();
|
| + }
|
| +
|
| + // Return no value if unconstrained.
|
| + if (set.is_universal()) {
|
| + return base::Optional<bool>();
|
| + }
|
| + DCHECK_EQ(set.elements().size(), 1U);
|
| + return set.FirstElement();
|
| +}
|
| +
|
| +base::Optional<std::string> SelectOptionalString(
|
| + const DiscreteSet<std::string>& set,
|
| + const blink::StringConstraint& constraint) {
|
| + DCHECK(!set.IsEmpty());
|
| + if (constraint.HasIdeal()) {
|
| + for (const auto& ideal_candidate : constraint.Ideal()) {
|
| + std::string candidate = ideal_candidate.Utf8();
|
| + if (set.Contains(candidate)) {
|
| + return candidate;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Return no value if unconstrained.
|
| + if (set.is_universal()) {
|
| + return base::Optional<std::string>();
|
| + }
|
| + return set.FirstElement();
|
| +}
|
| +
|
| +bool SelectEnableSwEchoCancellation(
|
| + base::Optional<bool> echo_cancellation,
|
| + base::Optional<bool> goog_echo_cancellation,
|
| + const media::AudioParameters& audio_parameters,
|
| + bool default_audio_processing_value) {
|
| + // If there is hardware echo cancellation, return false.
|
| + if (audio_parameters.IsValid() &&
|
| + (audio_parameters.effects() & media::AudioParameters::ECHO_CANCELLER))
|
| + return false;
|
| + DCHECK(echo_cancellation && goog_echo_cancellation
|
| + ? *echo_cancellation == *goog_echo_cancellation
|
| + : true);
|
| + if (echo_cancellation)
|
| + return *echo_cancellation;
|
| + if (goog_echo_cancellation)
|
| + return *goog_echo_cancellation;
|
| +
|
| + return default_audio_processing_value;
|
| +}
|
| +
|
| +struct AudioPropertyConstraintPair {
|
| + bool AudioProcessingProperties::*audio_property;
|
| + AudioCaptureCandidates::BoolConstraint bool_set_index;
|
| + blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*constraint;
|
| +};
|
| +
|
| +// Boolean audio properties that are mapped directly to a boolean constraint
|
| +// and which are subject to the same processing.
|
| +const AudioPropertyConstraintPair kAudioPropertyConstraintMap[] = {
|
| + {&AudioProcessingProperties::goog_auto_gain_control,
|
| + AudioCaptureCandidates::GOOG_AUTO_GAIN_CONTROL,
|
| + &blink::WebMediaTrackConstraintSet::goog_auto_gain_control},
|
| + {&AudioProcessingProperties::goog_experimental_echo_cancellation,
|
| + AudioCaptureCandidates::GOOG_EXPERIMENTAL_ECHO_CANCELLATION,
|
| + &blink::WebMediaTrackConstraintSet::goog_experimental_echo_cancellation},
|
| + {&AudioProcessingProperties::goog_typing_noise_detection,
|
| + AudioCaptureCandidates::GOOG_TYPING_NOISE_DETECTION,
|
| + &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection},
|
| + {&AudioProcessingProperties::goog_noise_suppression,
|
| + AudioCaptureCandidates::GOOG_NOISE_SUPPRESSION,
|
| + &blink::WebMediaTrackConstraintSet::goog_noise_suppression},
|
| + {&AudioProcessingProperties::goog_experimental_noise_suppression,
|
| + AudioCaptureCandidates::GOOG_EXPERIMENTAL_NOISE_SUPPRESSION,
|
| + &blink::WebMediaTrackConstraintSet::goog_experimental_noise_suppression},
|
| + {&AudioProcessingProperties::goog_beamforming,
|
| + AudioCaptureCandidates::GOOG_BEAMFORMING,
|
| + &blink::WebMediaTrackConstraintSet::goog_beamforming},
|
| + {&AudioProcessingProperties::goog_highpass_filter,
|
| + AudioCaptureCandidates::GOOG_HIGHPASS_FILTER,
|
| + &blink::WebMediaTrackConstraintSet::goog_highpass_filter},
|
| + {&AudioProcessingProperties::goog_experimental_auto_gain_control,
|
| + AudioCaptureCandidates::GOOG_EXPERIMENTAL_AUTO_GAIN_CONTROL,
|
| + &blink::WebMediaTrackConstraintSet::goog_experimental_auto_gain_control}};
|
| +
|
| +AudioProcessingProperties SelectAudioProcessingProperties(
|
| + const AudioCaptureCandidates& candidates,
|
| + const blink::WebMediaTrackConstraintSet& basic_constraint_set,
|
| + const media::AudioParameters& audio_parameters,
|
| + bool is_device_capture) {
|
| + DCHECK(!candidates.IsEmpty());
|
| + base::Optional<bool> echo_cancellation =
|
| + SelectOptionalBool(candidates.echo_cancellation_set(),
|
| + basic_constraint_set.echo_cancellation);
|
| + // Audio-processing properties are disabled by default for content capture, or
|
| + // if the |echo_cancellation| constraint is false.
|
| + bool default_audio_processing_value = true;
|
| + if (!is_device_capture || (echo_cancellation && !*echo_cancellation))
|
| + default_audio_processing_value = false;
|
| +
|
| + base::Optional<bool> goog_echo_cancellation =
|
| + SelectOptionalBool(candidates.goog_echo_cancellation_set(),
|
| + basic_constraint_set.goog_echo_cancellation);
|
| +
|
| + AudioProcessingProperties properties;
|
| + properties.enable_sw_echo_cancellation = SelectEnableSwEchoCancellation(
|
| + echo_cancellation, goog_echo_cancellation, audio_parameters,
|
| + default_audio_processing_value);
|
| + properties.disable_hw_echo_cancellation =
|
| + (echo_cancellation && !*echo_cancellation) ||
|
| + (goog_echo_cancellation && !*goog_echo_cancellation);
|
| +
|
| + properties.goog_audio_mirroring =
|
| + SelectBool(candidates.goog_audio_mirroring_set(),
|
| + basic_constraint_set.goog_audio_mirroring,
|
| + properties.goog_audio_mirroring);
|
| +
|
| + for (auto& entry : kAudioPropertyConstraintMap) {
|
| + properties.*entry.audio_property = SelectBool(
|
| + candidates.GetBoolSet(entry.bool_set_index),
|
| + basic_constraint_set.*entry.constraint,
|
| + default_audio_processing_value && properties.*entry.audio_property);
|
| + }
|
| +
|
| + base::Optional<std::string> array_geometry =
|
| + SelectOptionalString(candidates.goog_array_geometry_set(),
|
| + basic_constraint_set.goog_array_geometry);
|
| + std::vector<media::Point> parsed_positions;
|
| + if (array_geometry)
|
| + parsed_positions = media::ParsePointsFromString(*array_geometry);
|
| + bool are_valid_parsed_positions =
|
| + !parsed_positions.empty() || (array_geometry && array_geometry->empty());
|
| + properties.goog_array_geometry = are_valid_parsed_positions
|
| + ? std::move(parsed_positions)
|
| + : audio_parameters.mic_positions();
|
| +
|
| + return properties;
|
| +}
|
| +
|
| +AudioCaptureSettings SelectResult(
|
| + const AudioCaptureCandidates& candidates,
|
| + const blink::WebMediaTrackConstraintSet& basic_constraint_set,
|
| + const std::string& default_device_id,
|
| + const std::string& media_stream_source) {
|
| + bool is_device_capture = media_stream_source.empty();
|
| + AudioDeviceInfo device_info =
|
| + SelectDevice(candidates.audio_device_set(), basic_constraint_set,
|
| + default_device_id, is_device_capture);
|
| + bool hotword_enabled =
|
| + SelectBool(candidates.hotword_enabled_set(),
|
| + basic_constraint_set.hotword_enabled, false);
|
| + bool disable_local_echo_default =
|
| + media_stream_source != kMediaStreamSourceDesktop;
|
| + bool disable_local_echo = SelectBool(candidates.disable_local_echo_set(),
|
| + basic_constraint_set.disable_local_echo,
|
| + disable_local_echo_default);
|
| + bool render_to_associated_sink =
|
| + SelectBool(candidates.render_to_associated_sink_set(),
|
| + basic_constraint_set.render_to_associated_sink, false);
|
| +
|
| + AudioProcessingProperties audio_processing_properties =
|
| + SelectAudioProcessingProperties(candidates, basic_constraint_set,
|
| + device_info.parameters(),
|
| + is_device_capture);
|
| +
|
| + return AudioCaptureSettings(device_info.device_id(), device_info.parameters(),
|
| + hotword_enabled, disable_local_echo,
|
| + render_to_associated_sink,
|
| + audio_processing_properties);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +AudioCaptureSettings SelectSettingsAudioCapture(
|
| + const AudioDeviceCaptureCapabilities& capabilities,
|
| + const blink::WebMediaConstraints& constraints) {
|
| + std::string media_stream_source = GetMediaStreamSource(constraints);
|
| + bool is_device_capture = media_stream_source.empty();
|
| + if (is_device_capture && capabilities.empty())
|
| + return AudioCaptureSettings();
|
| +
|
| + AudioCaptureCandidates candidates(constraints.Basic(), capabilities,
|
| + is_device_capture);
|
| + if (candidates.IsEmpty()) {
|
| + return AudioCaptureSettings(candidates.failed_constraint_name());
|
| + }
|
| +
|
| + for (const auto& advanced_set : constraints.Advanced()) {
|
| + AudioCaptureCandidates advanced_candidates(advanced_set, capabilities,
|
| + is_device_capture);
|
| + AudioCaptureCandidates intersection =
|
| + candidates.Intersection(advanced_candidates);
|
| + if (!intersection.IsEmpty())
|
| + candidates = std::move(intersection);
|
| + }
|
| + DCHECK(!candidates.IsEmpty());
|
| +
|
| + std::string default_device_id;
|
| + if (!capabilities.empty())
|
| + default_device_id = (*capabilities.begin())->device_id;
|
| +
|
| + return SelectResult(candidates, constraints.Basic(), default_device_id,
|
| + media_stream_source);
|
| +}
|
| +
|
| +} // namespace content
|
|
|