Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(431)

Unified Diff: content/renderer/media/media_stream_constraints_util_audio.cc

Issue 2941553003: Reland "SelectSettings algorithm for audio constraints." (Closed)
Patch Set: fix test Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698