OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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_constraints_util_audio.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <cmath> |
| 9 #include <utility> |
| 10 #include <vector> |
| 11 |
| 12 #include "content/common/media/media_stream_options.h" |
| 13 #include "content/renderer/media/media_stream_constraints_util_sets.h" |
| 14 #include "content/renderer/media/media_stream_video_source.h" |
| 15 #include "media/base/limits.h" |
| 16 #include "third_party/WebKit/public/platform/WebMediaConstraints.h" |
| 17 #include "third_party/WebKit/public/platform/WebString.h" |
| 18 |
| 19 namespace content { |
| 20 |
| 21 namespace { |
| 22 |
| 23 // This class has the same data as ::mojom::AudioInputDeviceCapabilities, but |
| 24 // adds extra operations to simplify the device-selection code. |
| 25 class AudioDeviceInfo { |
| 26 public: |
| 27 // This constructor is intended for device capture. |
| 28 explicit AudioDeviceInfo( |
| 29 const ::mojom::AudioInputDeviceCapabilitiesPtr& device_info) |
| 30 : device_id_(device_info->device_id), |
| 31 parameters_(device_info->parameters) { |
| 32 DCHECK(parameters_.IsValid()); |
| 33 } |
| 34 |
| 35 // This constructor is intended for content capture, where constraints on |
| 36 // audio parameters are not supported. |
| 37 explicit AudioDeviceInfo(std::string device_id) |
| 38 : device_id_(std::move(device_id)) {} |
| 39 |
| 40 bool operator==(const AudioDeviceInfo& other) const { |
| 41 return device_id_ == other.device_id_; |
| 42 } |
| 43 |
| 44 // Accessors |
| 45 const std::string& device_id() const { return device_id_; } |
| 46 const media::AudioParameters& parameters() const { return parameters_; } |
| 47 |
| 48 // Convenience accessors |
| 49 int SampleRate() const { |
| 50 DCHECK(parameters_.IsValid()); |
| 51 return parameters_.sample_rate(); |
| 52 } |
| 53 int SampleSize() const { |
| 54 DCHECK(parameters_.IsValid()); |
| 55 return parameters_.bits_per_sample(); |
| 56 } |
| 57 int ChannelCount() const { |
| 58 DCHECK(parameters_.IsValid()); |
| 59 return parameters_.channels(); |
| 60 } |
| 61 int Effects() const { |
| 62 DCHECK(parameters_.IsValid()); |
| 63 return parameters_.effects(); |
| 64 } |
| 65 |
| 66 private: |
| 67 std::string device_id_; |
| 68 media::AudioParameters parameters_; |
| 69 }; |
| 70 |
| 71 using AudioDeviceSet = DiscreteSet<AudioDeviceInfo>; |
| 72 |
| 73 AudioDeviceSet AudioDeviceSetForDeviceCapture( |
| 74 const blink::WebMediaTrackConstraintSet& constraint_set, |
| 75 const AudioDeviceCaptureCapabilities& capabilities, |
| 76 const char** failed_constraint_name) { |
| 77 std::vector<AudioDeviceInfo> result; |
| 78 *failed_constraint_name = ""; |
| 79 for (auto& device_capabilities : capabilities) { |
| 80 if (!constraint_set.device_id.Matches( |
| 81 blink::WebString::FromASCII(device_capabilities->device_id))) { |
| 82 if (failed_constraint_name) |
| 83 *failed_constraint_name = constraint_set.device_id.GetName(); |
| 84 continue; |
| 85 } |
| 86 *failed_constraint_name = |
| 87 IsOutsideConstraintRange(constraint_set.sample_rate, |
| 88 device_capabilities->parameters.sample_rate()); |
| 89 if (*failed_constraint_name) |
| 90 continue; |
| 91 |
| 92 *failed_constraint_name = IsOutsideConstraintRange( |
| 93 constraint_set.sample_size, |
| 94 device_capabilities->parameters.bits_per_sample()); |
| 95 if (*failed_constraint_name) |
| 96 continue; |
| 97 |
| 98 *failed_constraint_name = |
| 99 IsOutsideConstraintRange(constraint_set.channel_count, |
| 100 device_capabilities->parameters.channels()); |
| 101 if (*failed_constraint_name) |
| 102 continue; |
| 103 |
| 104 result.push_back(AudioDeviceInfo(device_capabilities)); |
| 105 } |
| 106 |
| 107 if (!result.empty()) |
| 108 *failed_constraint_name = nullptr; |
| 109 |
| 110 return AudioDeviceSet(result); |
| 111 } |
| 112 |
| 113 AudioDeviceSet AudioDeviceSetForContentCapture( |
| 114 const blink::WebMediaTrackConstraintSet& constraint_set, |
| 115 const char** failed_constraint_name = nullptr) { |
| 116 if (!constraint_set.device_id.HasExact()) |
| 117 return AudioDeviceSet::UniversalSet(); |
| 118 |
| 119 std::vector<AudioDeviceInfo> result; |
| 120 for (auto& device_id : constraint_set.device_id.Exact()) |
| 121 result.push_back(AudioDeviceInfo(device_id.Utf8())); |
| 122 |
| 123 return AudioDeviceSet(result); |
| 124 } |
| 125 |
| 126 // This class represents a set of possible candidate settings. |
| 127 // The SelectSettings algorithm starts with a set containing all possible |
| 128 // candidates based on hardware capabilities and/or allowed values for supported |
| 129 // properties. The is then reduced progressively as the basic and advanced |
| 130 // constraint sets are applied. |
| 131 // In the end, if the set of candidates is empty, SelectSettings fails. |
| 132 // If not, the ideal values (if any) or tie breaker rules are used to select |
| 133 // the final settings based on the candidates that survived the application |
| 134 // of the constraint sets. |
| 135 // This class is implemented as a collection of more specific sets for the |
| 136 // various supported properties. If any of the specific sets is empty, the |
| 137 // whole AudioCaptureCandidates set is considered empty as well. |
| 138 class AudioCaptureCandidates { |
| 139 public: |
| 140 enum BoolConstraint { |
| 141 // Constraints not related to audio processing. |
| 142 HOTWORD_ENABLED, |
| 143 DISABLE_LOCAL_ECHO, |
| 144 RENDER_TO_ASSOCIATED_SINK, |
| 145 |
| 146 // Constraints that enable/disable audio processing. |
| 147 ECHO_CANCELLATION, |
| 148 GOOG_ECHO_CANCELLATION, |
| 149 |
| 150 // Constraints that control audio-processing behavior. |
| 151 GOOG_AUDIO_MIRRORING, |
| 152 GOOG_AUTO_GAIN_CONTROL, |
| 153 GOOG_EXPERIMENTAL_ECHO_CANCELLATION, |
| 154 GOOG_TYPING_NOISE_DETECTION, |
| 155 GOOG_NOISE_SUPPRESSION, |
| 156 GOOG_EXPERIMENTAL_NOISE_SUPPRESSION, |
| 157 GOOG_BEAMFORMING, |
| 158 GOOG_HIGHPASS_FILTER, |
| 159 GOOG_EXPERIMENTAL_AUTO_GAIN_CONTROL, |
| 160 NUM_BOOL_CONSTRAINTS |
| 161 }; |
| 162 |
| 163 AudioCaptureCandidates(); |
| 164 |
| 165 AudioCaptureCandidates( |
| 166 const blink::WebMediaTrackConstraintSet& constraint_set, |
| 167 const AudioDeviceCaptureCapabilities& capabilities, |
| 168 bool is_device_capture); |
| 169 |
| 170 // Set operations. |
| 171 bool IsEmpty() const { return failed_constraint_name_ != nullptr; } |
| 172 AudioCaptureCandidates Intersection(const AudioCaptureCandidates& other); |
| 173 |
| 174 // Accessors. |
| 175 const char* failed_constraint_name() const { return failed_constraint_name_; } |
| 176 const AudioDeviceSet& audio_device_set() const { return audio_device_set_; } |
| 177 const DiscreteSet<std::string>& goog_array_geometry_set() const { |
| 178 return goog_array_geometry_set_; |
| 179 } |
| 180 |
| 181 // Accessor for boolean sets. |
| 182 const DiscreteSet<bool>& GetBoolSet(BoolConstraint property) const { |
| 183 DCHECK_GE(property, 0); |
| 184 DCHECK_LT(property, NUM_BOOL_CONSTRAINTS); |
| 185 return bool_sets_[property]; |
| 186 } |
| 187 |
| 188 // Convenience accessors. |
| 189 const DiscreteSet<bool>& hotword_enabled_set() const { |
| 190 return bool_sets_[HOTWORD_ENABLED]; |
| 191 } |
| 192 const DiscreteSet<bool>& disable_local_echo_set() const { |
| 193 return bool_sets_[DISABLE_LOCAL_ECHO]; |
| 194 } |
| 195 const DiscreteSet<bool>& render_to_associated_sink_set() const { |
| 196 return bool_sets_[RENDER_TO_ASSOCIATED_SINK]; |
| 197 } |
| 198 const DiscreteSet<bool>& echo_cancellation_set() const { |
| 199 return bool_sets_[ECHO_CANCELLATION]; |
| 200 } |
| 201 const DiscreteSet<bool>& goog_echo_cancellation_set() const { |
| 202 return bool_sets_[GOOG_ECHO_CANCELLATION]; |
| 203 } |
| 204 const DiscreteSet<bool>& goog_audio_mirroring_set() const { |
| 205 return bool_sets_[GOOG_AUDIO_MIRRORING]; |
| 206 } |
| 207 |
| 208 private: |
| 209 void MaybeUpdateFailedNonDeviceConstraintName(); |
| 210 void CheckContradictoryEchoCancellation(); |
| 211 |
| 212 // Maps BoolConstraint values to fields in blink::WebMediaTrackConstraintSet. |
| 213 static const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::* |
| 214 kBlinkBoolConstraintFields[NUM_BOOL_CONSTRAINTS]; |
| 215 |
| 216 const char* failed_constraint_name_; |
| 217 |
| 218 AudioDeviceSet audio_device_set_; // Device-related constraints. |
| 219 std::array<DiscreteSet<bool>, NUM_BOOL_CONSTRAINTS> bool_sets_; |
| 220 DiscreteSet<std::string> goog_array_geometry_set_; |
| 221 }; |
| 222 |
| 223 const blink::BooleanConstraint blink::WebMediaTrackConstraintSet::* |
| 224 AudioCaptureCandidates::kBlinkBoolConstraintFields[NUM_BOOL_CONSTRAINTS] = { |
| 225 &blink::WebMediaTrackConstraintSet::hotword_enabled, |
| 226 &blink::WebMediaTrackConstraintSet::disable_local_echo, |
| 227 &blink::WebMediaTrackConstraintSet::render_to_associated_sink, |
| 228 &blink::WebMediaTrackConstraintSet::echo_cancellation, |
| 229 &blink::WebMediaTrackConstraintSet::goog_echo_cancellation, |
| 230 &blink::WebMediaTrackConstraintSet::goog_audio_mirroring, |
| 231 &blink::WebMediaTrackConstraintSet::goog_auto_gain_control, |
| 232 &blink::WebMediaTrackConstraintSet::goog_experimental_echo_cancellation, |
| 233 &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection, |
| 234 &blink::WebMediaTrackConstraintSet::goog_noise_suppression, |
| 235 &blink::WebMediaTrackConstraintSet::goog_experimental_noise_suppression, |
| 236 &blink::WebMediaTrackConstraintSet::goog_beamforming, |
| 237 &blink::WebMediaTrackConstraintSet::goog_highpass_filter, |
| 238 &blink::WebMediaTrackConstraintSet:: |
| 239 goog_experimental_auto_gain_control}; |
| 240 |
| 241 // directly mapped boolean sets to audio properties |
| 242 struct BoolSetPropertyEntry { |
| 243 DiscreteSet<bool> AudioCaptureCandidates::*bool_set; |
| 244 bool AudioProcessingProperties::*bool_field; |
| 245 bool default_value; |
| 246 }; |
| 247 |
| 248 AudioCaptureCandidates::AudioCaptureCandidates() |
| 249 : failed_constraint_name_(nullptr) {} |
| 250 |
| 251 AudioCaptureCandidates::AudioCaptureCandidates( |
| 252 const blink::WebMediaTrackConstraintSet& constraint_set, |
| 253 const AudioDeviceCaptureCapabilities& capabilities, |
| 254 bool is_device_capture) |
| 255 : failed_constraint_name_(nullptr), |
| 256 audio_device_set_( |
| 257 is_device_capture |
| 258 ? AudioDeviceSetForDeviceCapture(constraint_set, |
| 259 capabilities, |
| 260 &failed_constraint_name_) |
| 261 : AudioDeviceSetForContentCapture(constraint_set, |
| 262 &failed_constraint_name_)), |
| 263 goog_array_geometry_set_( |
| 264 StringSetFromConstraint(constraint_set.goog_array_geometry)) { |
| 265 for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) { |
| 266 bool_sets_[i] = |
| 267 BoolSetFromConstraint(constraint_set.*kBlinkBoolConstraintFields[i]); |
| 268 } |
| 269 MaybeUpdateFailedNonDeviceConstraintName(); |
| 270 } |
| 271 |
| 272 AudioCaptureCandidates AudioCaptureCandidates::Intersection( |
| 273 const AudioCaptureCandidates& other) { |
| 274 AudioCaptureCandidates intersection; |
| 275 intersection.audio_device_set_ = |
| 276 audio_device_set_.Intersection(other.audio_device_set_); |
| 277 if (intersection.audio_device_set_.IsEmpty()) { |
| 278 // To mark the intersection as empty, it is necessary to assign a |
| 279 // a non-null value to |failed_constraint_name_|. The specific value |
| 280 // for an intersection does not actually matter, since the intersection |
| 281 // is discarded if empty. |
| 282 intersection.failed_constraint_name_ = "some device constraint"; |
| 283 return intersection; |
| 284 } |
| 285 for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) { |
| 286 intersection.bool_sets_[i] = |
| 287 bool_sets_[i].Intersection(other.bool_sets_[i]); |
| 288 } |
| 289 intersection.goog_array_geometry_set_ = |
| 290 goog_array_geometry_set_.Intersection(other.goog_array_geometry_set_); |
| 291 intersection.MaybeUpdateFailedNonDeviceConstraintName(); |
| 292 |
| 293 return intersection; |
| 294 } |
| 295 |
| 296 void AudioCaptureCandidates::MaybeUpdateFailedNonDeviceConstraintName() { |
| 297 blink::WebMediaTrackConstraintSet constraint_set; |
| 298 for (size_t i = 0; i < NUM_BOOL_CONSTRAINTS; ++i) { |
| 299 if (bool_sets_[i].IsEmpty()) { |
| 300 failed_constraint_name_ = |
| 301 (constraint_set.*kBlinkBoolConstraintFields[i]).GetName(); |
| 302 } |
| 303 } |
| 304 if (goog_array_geometry_set_.IsEmpty()) |
| 305 failed_constraint_name_ = constraint_set.goog_array_geometry.GetName(); |
| 306 |
| 307 CheckContradictoryEchoCancellation(); |
| 308 } |
| 309 |
| 310 void AudioCaptureCandidates::CheckContradictoryEchoCancellation() { |
| 311 DiscreteSet<bool> echo_cancellation_intersection = |
| 312 bool_sets_[ECHO_CANCELLATION].Intersection( |
| 313 bool_sets_[GOOG_ECHO_CANCELLATION]); |
| 314 // echoCancellation and googEchoCancellation constraints should not |
| 315 // contradict each other. Mark the set as empty if they do. |
| 316 if (echo_cancellation_intersection.IsEmpty()) { |
| 317 failed_constraint_name_ = |
| 318 blink::WebMediaTrackConstraintSet().echo_cancellation.GetName(); |
| 319 } |
| 320 } |
| 321 |
| 322 // Fitness function for constraints involved in device selection. |
| 323 // Based on https://w3c.github.io/mediacapture-main/#dfn-fitness-distance |
| 324 double DeviceInfoFitness( |
| 325 bool is_device_capture, |
| 326 const AudioDeviceInfo& device_info, |
| 327 const blink::WebMediaTrackConstraintSet& basic_constraint_set) { |
| 328 double fitness = 0.0; |
| 329 fitness += StringConstraintFitnessDistance( |
| 330 blink::WebString::FromASCII(device_info.device_id()), |
| 331 basic_constraint_set.device_id); |
| 332 |
| 333 if (!is_device_capture) |
| 334 return fitness; |
| 335 |
| 336 if (basic_constraint_set.sample_rate.HasIdeal()) { |
| 337 fitness += NumericConstraintFitnessDistance( |
| 338 device_info.SampleRate(), basic_constraint_set.sample_rate.Ideal()); |
| 339 } |
| 340 |
| 341 if (basic_constraint_set.sample_size.HasIdeal()) { |
| 342 fitness += NumericConstraintFitnessDistance( |
| 343 device_info.SampleSize(), basic_constraint_set.sample_size.Ideal()); |
| 344 } |
| 345 |
| 346 if (basic_constraint_set.channel_count.HasIdeal()) { |
| 347 fitness += NumericConstraintFitnessDistance( |
| 348 device_info.ChannelCount(), basic_constraint_set.channel_count.Ideal()); |
| 349 } |
| 350 |
| 351 return fitness; |
| 352 } |
| 353 |
| 354 AudioDeviceInfo SelectDevice( |
| 355 const AudioDeviceSet& audio_device_set, |
| 356 const blink::WebMediaTrackConstraintSet& basic_constraint_set, |
| 357 const std::string& default_device_id, |
| 358 bool is_device_capture) { |
| 359 DCHECK(!audio_device_set.IsEmpty()); |
| 360 if (audio_device_set.is_universal()) { |
| 361 DCHECK(!is_device_capture); |
| 362 std::string device_id; |
| 363 if (basic_constraint_set.device_id.HasIdeal()) { |
| 364 device_id = basic_constraint_set.device_id.Ideal().begin()->Utf8(); |
| 365 } |
| 366 return AudioDeviceInfo(std::move(device_id)); |
| 367 } |
| 368 |
| 369 std::vector<double> best_fitness({HUGE_VAL, HUGE_VAL}); |
| 370 auto best_candidate = audio_device_set.elements().end(); |
| 371 for (auto it = audio_device_set.elements().begin(); |
| 372 it != audio_device_set.elements().end(); ++it) { |
| 373 std::vector<double> fitness; |
| 374 // First criterion is spec-based fitness function. Second criterion is |
| 375 // being the default device. |
| 376 fitness.push_back( |
| 377 DeviceInfoFitness(is_device_capture, *it, basic_constraint_set)); |
| 378 fitness.push_back(it->device_id() == default_device_id ? 0.0 : HUGE_VAL); |
| 379 if (fitness < best_fitness) { |
| 380 best_fitness = std::move(fitness); |
| 381 best_candidate = it; |
| 382 } |
| 383 } |
| 384 DCHECK(best_candidate != audio_device_set.elements().end()); |
| 385 return *best_candidate; |
| 386 } |
| 387 |
| 388 bool SelectBool(const DiscreteSet<bool>& set, |
| 389 const blink::BooleanConstraint& constraint, |
| 390 bool default_value) { |
| 391 DCHECK(!set.IsEmpty()); |
| 392 if (constraint.HasIdeal() && set.Contains(constraint.Ideal())) { |
| 393 return constraint.Ideal(); |
| 394 } |
| 395 |
| 396 // Return default value if unconstrained. |
| 397 if (set.is_universal()) { |
| 398 return default_value; |
| 399 } |
| 400 DCHECK_EQ(set.elements().size(), 1U); |
| 401 return set.FirstElement(); |
| 402 } |
| 403 |
| 404 base::Optional<bool> SelectOptionalBool( |
| 405 const DiscreteSet<bool>& set, |
| 406 const blink::BooleanConstraint& constraint) { |
| 407 DCHECK(!set.IsEmpty()); |
| 408 if (constraint.HasIdeal() && set.Contains(constraint.Ideal())) { |
| 409 return constraint.Ideal(); |
| 410 } |
| 411 |
| 412 // Return no value if unconstrained. |
| 413 if (set.is_universal()) { |
| 414 return base::Optional<bool>(); |
| 415 } |
| 416 DCHECK_EQ(set.elements().size(), 1U); |
| 417 return set.FirstElement(); |
| 418 } |
| 419 |
| 420 base::Optional<std::string> SelectOptionalString( |
| 421 const DiscreteSet<std::string>& set, |
| 422 const blink::StringConstraint& constraint) { |
| 423 DCHECK(!set.IsEmpty()); |
| 424 if (constraint.HasIdeal()) { |
| 425 for (const auto& ideal_candidate : constraint.Ideal()) { |
| 426 std::string candidate = ideal_candidate.Utf8(); |
| 427 if (set.Contains(candidate)) { |
| 428 return candidate; |
| 429 } |
| 430 } |
| 431 } |
| 432 |
| 433 // Return no value if unconstrained. |
| 434 if (set.is_universal()) { |
| 435 return base::Optional<std::string>(); |
| 436 } |
| 437 return set.FirstElement(); |
| 438 } |
| 439 |
| 440 bool SelectEnableSwEchoCancellation( |
| 441 base::Optional<bool> echo_cancellation, |
| 442 base::Optional<bool> goog_echo_cancellation, |
| 443 const media::AudioParameters& audio_parameters, |
| 444 bool default_audio_processing_value) { |
| 445 // If there is hardware echo cancellation, return false. |
| 446 if (audio_parameters.IsValid() && |
| 447 (audio_parameters.effects() & media::AudioParameters::ECHO_CANCELLER)) |
| 448 return false; |
| 449 DCHECK(echo_cancellation && goog_echo_cancellation |
| 450 ? *echo_cancellation == *goog_echo_cancellation |
| 451 : true); |
| 452 if (echo_cancellation) |
| 453 return *echo_cancellation; |
| 454 if (goog_echo_cancellation) |
| 455 return *goog_echo_cancellation; |
| 456 |
| 457 return default_audio_processing_value; |
| 458 } |
| 459 |
| 460 struct AudioPropertyConstraintPair { |
| 461 bool AudioProcessingProperties::*audio_property; |
| 462 AudioCaptureCandidates::BoolConstraint bool_set_index; |
| 463 blink::BooleanConstraint blink::WebMediaTrackConstraintSet::*constraint; |
| 464 }; |
| 465 |
| 466 // Boolean audio properties that are mapped directly to a boolean constraint |
| 467 // and which are subject to the same processing. |
| 468 const AudioPropertyConstraintPair kAudioPropertyConstraintMap[] = { |
| 469 {&AudioProcessingProperties::goog_auto_gain_control, |
| 470 AudioCaptureCandidates::GOOG_AUTO_GAIN_CONTROL, |
| 471 &blink::WebMediaTrackConstraintSet::goog_auto_gain_control}, |
| 472 {&AudioProcessingProperties::goog_experimental_echo_cancellation, |
| 473 AudioCaptureCandidates::GOOG_EXPERIMENTAL_ECHO_CANCELLATION, |
| 474 &blink::WebMediaTrackConstraintSet::goog_experimental_echo_cancellation}, |
| 475 {&AudioProcessingProperties::goog_typing_noise_detection, |
| 476 AudioCaptureCandidates::GOOG_TYPING_NOISE_DETECTION, |
| 477 &blink::WebMediaTrackConstraintSet::goog_typing_noise_detection}, |
| 478 {&AudioProcessingProperties::goog_noise_suppression, |
| 479 AudioCaptureCandidates::GOOG_NOISE_SUPPRESSION, |
| 480 &blink::WebMediaTrackConstraintSet::goog_noise_suppression}, |
| 481 {&AudioProcessingProperties::goog_experimental_noise_suppression, |
| 482 AudioCaptureCandidates::GOOG_EXPERIMENTAL_NOISE_SUPPRESSION, |
| 483 &blink::WebMediaTrackConstraintSet::goog_experimental_noise_suppression}, |
| 484 {&AudioProcessingProperties::goog_beamforming, |
| 485 AudioCaptureCandidates::GOOG_BEAMFORMING, |
| 486 &blink::WebMediaTrackConstraintSet::goog_beamforming}, |
| 487 {&AudioProcessingProperties::goog_highpass_filter, |
| 488 AudioCaptureCandidates::GOOG_HIGHPASS_FILTER, |
| 489 &blink::WebMediaTrackConstraintSet::goog_highpass_filter}, |
| 490 {&AudioProcessingProperties::goog_experimental_auto_gain_control, |
| 491 AudioCaptureCandidates::GOOG_EXPERIMENTAL_AUTO_GAIN_CONTROL, |
| 492 &blink::WebMediaTrackConstraintSet::goog_experimental_auto_gain_control}}; |
| 493 |
| 494 AudioProcessingProperties SelectAudioProcessingProperties( |
| 495 const AudioCaptureCandidates& candidates, |
| 496 const blink::WebMediaTrackConstraintSet& basic_constraint_set, |
| 497 const media::AudioParameters& audio_parameters, |
| 498 bool is_device_capture) { |
| 499 DCHECK(!candidates.IsEmpty()); |
| 500 base::Optional<bool> echo_cancellation = |
| 501 SelectOptionalBool(candidates.echo_cancellation_set(), |
| 502 basic_constraint_set.echo_cancellation); |
| 503 // Audio-processing properties are disabled by default for content capture, or |
| 504 // if the |echo_cancellation| constraint is false. |
| 505 bool default_audio_processing_value = true; |
| 506 if (!is_device_capture || (echo_cancellation && !*echo_cancellation)) |
| 507 default_audio_processing_value = false; |
| 508 |
| 509 base::Optional<bool> goog_echo_cancellation = |
| 510 SelectOptionalBool(candidates.goog_echo_cancellation_set(), |
| 511 basic_constraint_set.goog_echo_cancellation); |
| 512 |
| 513 AudioProcessingProperties properties; |
| 514 properties.enable_sw_echo_cancellation = SelectEnableSwEchoCancellation( |
| 515 echo_cancellation, goog_echo_cancellation, audio_parameters, |
| 516 default_audio_processing_value); |
| 517 properties.disable_hw_echo_cancellation = |
| 518 (echo_cancellation && !*echo_cancellation) || |
| 519 (goog_echo_cancellation && !*goog_echo_cancellation); |
| 520 |
| 521 properties.goog_audio_mirroring = |
| 522 SelectBool(candidates.goog_audio_mirroring_set(), |
| 523 basic_constraint_set.goog_audio_mirroring, |
| 524 properties.goog_audio_mirroring); |
| 525 |
| 526 for (auto& entry : kAudioPropertyConstraintMap) { |
| 527 properties.*entry.audio_property = SelectBool( |
| 528 candidates.GetBoolSet(entry.bool_set_index), |
| 529 basic_constraint_set.*entry.constraint, |
| 530 default_audio_processing_value && properties.*entry.audio_property); |
| 531 } |
| 532 |
| 533 base::Optional<std::string> array_geometry = |
| 534 SelectOptionalString(candidates.goog_array_geometry_set(), |
| 535 basic_constraint_set.goog_array_geometry); |
| 536 std::vector<media::Point> parsed_positions; |
| 537 if (array_geometry) |
| 538 parsed_positions = media::ParsePointsFromString(*array_geometry); |
| 539 bool are_valid_parsed_positions = |
| 540 !parsed_positions.empty() || (array_geometry && array_geometry->empty()); |
| 541 properties.goog_array_geometry = are_valid_parsed_positions |
| 542 ? std::move(parsed_positions) |
| 543 : audio_parameters.mic_positions(); |
| 544 |
| 545 return properties; |
| 546 } |
| 547 |
| 548 AudioCaptureSettings SelectResult( |
| 549 const AudioCaptureCandidates& candidates, |
| 550 const blink::WebMediaTrackConstraintSet& basic_constraint_set, |
| 551 const std::string& default_device_id, |
| 552 const std::string& media_stream_source) { |
| 553 bool is_device_capture = media_stream_source.empty(); |
| 554 AudioDeviceInfo device_info = |
| 555 SelectDevice(candidates.audio_device_set(), basic_constraint_set, |
| 556 default_device_id, is_device_capture); |
| 557 bool hotword_enabled = |
| 558 SelectBool(candidates.hotword_enabled_set(), |
| 559 basic_constraint_set.hotword_enabled, false); |
| 560 bool disable_local_echo_default = |
| 561 media_stream_source != kMediaStreamSourceDesktop; |
| 562 bool disable_local_echo = SelectBool(candidates.disable_local_echo_set(), |
| 563 basic_constraint_set.disable_local_echo, |
| 564 disable_local_echo_default); |
| 565 bool render_to_associated_sink = |
| 566 SelectBool(candidates.render_to_associated_sink_set(), |
| 567 basic_constraint_set.render_to_associated_sink, false); |
| 568 |
| 569 AudioProcessingProperties audio_processing_properties = |
| 570 SelectAudioProcessingProperties(candidates, basic_constraint_set, |
| 571 device_info.parameters(), |
| 572 is_device_capture); |
| 573 |
| 574 return AudioCaptureSettings(device_info.device_id(), device_info.parameters(), |
| 575 hotword_enabled, disable_local_echo, |
| 576 render_to_associated_sink, |
| 577 audio_processing_properties); |
| 578 } |
| 579 |
| 580 } // namespace |
| 581 |
| 582 AudioCaptureSettings SelectSettingsAudioCapture( |
| 583 const AudioDeviceCaptureCapabilities& capabilities, |
| 584 const blink::WebMediaConstraints& constraints) { |
| 585 std::string media_stream_source = GetMediaStreamSource(constraints); |
| 586 bool is_device_capture = media_stream_source.empty(); |
| 587 if (is_device_capture && capabilities.empty()) |
| 588 return AudioCaptureSettings(); |
| 589 |
| 590 AudioCaptureCandidates candidates(constraints.Basic(), capabilities, |
| 591 is_device_capture); |
| 592 if (candidates.IsEmpty()) { |
| 593 return AudioCaptureSettings(candidates.failed_constraint_name()); |
| 594 } |
| 595 |
| 596 for (const auto& advanced_set : constraints.Advanced()) { |
| 597 AudioCaptureCandidates advanced_candidates(advanced_set, capabilities, |
| 598 is_device_capture); |
| 599 AudioCaptureCandidates intersection = |
| 600 candidates.Intersection(advanced_candidates); |
| 601 if (!intersection.IsEmpty()) |
| 602 candidates = std::move(intersection); |
| 603 } |
| 604 DCHECK(!candidates.IsEmpty()); |
| 605 |
| 606 std::string default_device_id; |
| 607 if (!capabilities.empty()) |
| 608 default_device_id = (*capabilities.begin())->device_id; |
| 609 |
| 610 return SelectResult(candidates, constraints.Basic(), default_device_id, |
| 611 media_stream_source); |
| 612 } |
| 613 |
| 614 } // namespace content |
OLD | NEW |