| /* | 
 |  * Copyright (C) 2012 Google Inc. All rights reserved. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions are | 
 |  * met: | 
 |  * | 
 |  *     * Redistributions of source code must retain the above copyright | 
 |  * notice, this list of conditions and the following disclaimer. | 
 |  *     * Redistributions in binary form must reproduce the above | 
 |  * copyright notice, this list of conditions and the following disclaimer | 
 |  * in the documentation and/or other materials provided with the | 
 |  * distribution. | 
 |  *     * Neither the name of Google Inc. nor the names of its | 
 |  * contributors may be used to endorse or promote products derived from | 
 |  * this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
 |  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
 |  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
 |  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
 |  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "public/platform/WebMediaConstraints.h" | 
 |  | 
 | #include <math.h> | 
 | #include "base/memory/scoped_refptr.h" | 
 | #include "platform/wtf/ThreadSafeRefCounted.h" | 
 | #include "platform/wtf/text/StringBuilder.h" | 
 | #include "platform/wtf/text/WTFString.h" | 
 |  | 
 | namespace blink { | 
 |  | 
 | namespace { | 
 |  | 
 | template <typename T> | 
 | void MaybeEmitNamedValue(StringBuilder& builder, | 
 |                          bool emit, | 
 |                          const char* name, | 
 |                          T value) { | 
 |   if (!emit) | 
 |     return; | 
 |   if (builder.length() > 1) | 
 |     builder.Append(", "); | 
 |   builder.Append(name); | 
 |   builder.Append(": "); | 
 |   builder.AppendNumber(value); | 
 | } | 
 |  | 
 | void MaybeEmitNamedBoolean(StringBuilder& builder, | 
 |                            bool emit, | 
 |                            const char* name, | 
 |                            bool value) { | 
 |   if (!emit) | 
 |     return; | 
 |   if (builder.length() > 1) | 
 |     builder.Append(", "); | 
 |   builder.Append(name); | 
 |   builder.Append(": "); | 
 |   if (value) | 
 |     builder.Append("true"); | 
 |   else | 
 |     builder.Append("false"); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class WebMediaConstraintsPrivate final | 
 |     : public ThreadSafeRefCounted<WebMediaConstraintsPrivate> { | 
 |  public: | 
 |   static scoped_refptr<WebMediaConstraintsPrivate> Create(); | 
 |   static scoped_refptr<WebMediaConstraintsPrivate> Create( | 
 |       const WebMediaTrackConstraintSet& basic, | 
 |       const WebVector<WebMediaTrackConstraintSet>& advanced); | 
 |  | 
 |   bool IsEmpty() const; | 
 |   const WebMediaTrackConstraintSet& Basic() const; | 
 |   const WebVector<WebMediaTrackConstraintSet>& Advanced() const; | 
 |   const String ToString() const; | 
 |  | 
 |  private: | 
 |   WebMediaConstraintsPrivate( | 
 |       const WebMediaTrackConstraintSet& basic, | 
 |       const WebVector<WebMediaTrackConstraintSet>& advanced); | 
 |  | 
 |   WebMediaTrackConstraintSet basic_; | 
 |   WebVector<WebMediaTrackConstraintSet> advanced_; | 
 | }; | 
 |  | 
 | scoped_refptr<WebMediaConstraintsPrivate> WebMediaConstraintsPrivate::Create() { | 
 |   WebMediaTrackConstraintSet basic; | 
 |   WebVector<WebMediaTrackConstraintSet> advanced; | 
 |   return base::AdoptRef(new WebMediaConstraintsPrivate(basic, advanced)); | 
 | } | 
 |  | 
 | scoped_refptr<WebMediaConstraintsPrivate> WebMediaConstraintsPrivate::Create( | 
 |     const WebMediaTrackConstraintSet& basic, | 
 |     const WebVector<WebMediaTrackConstraintSet>& advanced) { | 
 |   return base::AdoptRef(new WebMediaConstraintsPrivate(basic, advanced)); | 
 | } | 
 |  | 
 | WebMediaConstraintsPrivate::WebMediaConstraintsPrivate( | 
 |     const WebMediaTrackConstraintSet& basic, | 
 |     const WebVector<WebMediaTrackConstraintSet>& advanced) | 
 |     : basic_(basic), advanced_(advanced) {} | 
 |  | 
 | bool WebMediaConstraintsPrivate::IsEmpty() const { | 
 |   // TODO(hta): When generating advanced constraints, make sure no empty | 
 |   // elements can be added to the m_advanced vector. | 
 |   return basic_.IsEmpty() && advanced_.empty(); | 
 | } | 
 |  | 
 | const WebMediaTrackConstraintSet& WebMediaConstraintsPrivate::Basic() const { | 
 |   return basic_; | 
 | } | 
 |  | 
 | const WebVector<WebMediaTrackConstraintSet>& | 
 | WebMediaConstraintsPrivate::Advanced() const { | 
 |   return advanced_; | 
 | } | 
 |  | 
 | const String WebMediaConstraintsPrivate::ToString() const { | 
 |   StringBuilder builder; | 
 |   if (!IsEmpty()) { | 
 |     builder.Append('{'); | 
 |     builder.Append(Basic().ToString()); | 
 |     if (!Advanced().empty()) { | 
 |       if (builder.length() > 1) | 
 |         builder.Append(", "); | 
 |       builder.Append("advanced: ["); | 
 |       bool first = true; | 
 |       for (const auto& constraint_set : Advanced()) { | 
 |         if (!first) | 
 |           builder.Append(", "); | 
 |         builder.Append('{'); | 
 |         builder.Append(constraint_set.ToString()); | 
 |         builder.Append('}'); | 
 |         first = false; | 
 |       } | 
 |       builder.Append(']'); | 
 |     } | 
 |     builder.Append('}'); | 
 |   } | 
 |   return builder.ToString(); | 
 | } | 
 |  | 
 | // *Constraints | 
 |  | 
 | BaseConstraint::BaseConstraint(const char* name) : name_(name) {} | 
 |  | 
 | BaseConstraint::~BaseConstraint() = default; | 
 |  | 
 | LongConstraint::LongConstraint(const char* name) | 
 |     : BaseConstraint(name), | 
 |       min_(), | 
 |       max_(), | 
 |       exact_(), | 
 |       ideal_(), | 
 |       has_min_(false), | 
 |       has_max_(false), | 
 |       has_exact_(false), | 
 |       has_ideal_(false) {} | 
 |  | 
 | bool LongConstraint::Matches(long value) const { | 
 |   if (has_min_ && value < min_) { | 
 |     return false; | 
 |   } | 
 |   if (has_max_ && value > max_) { | 
 |     return false; | 
 |   } | 
 |   if (has_exact_ && value != exact_) { | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool LongConstraint::IsEmpty() const { | 
 |   return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_; | 
 | } | 
 |  | 
 | bool LongConstraint::HasMandatory() const { | 
 |   return has_min_ || has_max_ || has_exact_; | 
 | } | 
 |  | 
 | WebString LongConstraint::ToString() const { | 
 |   StringBuilder builder; | 
 |   builder.Append('{'); | 
 |   MaybeEmitNamedValue(builder, has_min_, "min", min_); | 
 |   MaybeEmitNamedValue(builder, has_max_, "max", max_); | 
 |   MaybeEmitNamedValue(builder, has_exact_, "exact", exact_); | 
 |   MaybeEmitNamedValue(builder, has_ideal_, "ideal", ideal_); | 
 |   builder.Append('}'); | 
 |   return builder.ToString(); | 
 | } | 
 |  | 
 | const double DoubleConstraint::kConstraintEpsilon = 0.00001; | 
 |  | 
 | DoubleConstraint::DoubleConstraint(const char* name) | 
 |     : BaseConstraint(name), | 
 |       min_(), | 
 |       max_(), | 
 |       exact_(), | 
 |       ideal_(), | 
 |       has_min_(false), | 
 |       has_max_(false), | 
 |       has_exact_(false), | 
 |       has_ideal_(false) {} | 
 |  | 
 | bool DoubleConstraint::Matches(double value) const { | 
 |   if (has_min_ && value < min_ - kConstraintEpsilon) { | 
 |     return false; | 
 |   } | 
 |   if (has_max_ && value > max_ + kConstraintEpsilon) { | 
 |     return false; | 
 |   } | 
 |   if (has_exact_ && | 
 |       fabs(static_cast<double>(value) - exact_) > kConstraintEpsilon) { | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool DoubleConstraint::IsEmpty() const { | 
 |   return !has_min_ && !has_max_ && !has_exact_ && !has_ideal_; | 
 | } | 
 |  | 
 | bool DoubleConstraint::HasMandatory() const { | 
 |   return has_min_ || has_max_ || has_exact_; | 
 | } | 
 |  | 
 | WebString DoubleConstraint::ToString() const { | 
 |   StringBuilder builder; | 
 |   builder.Append('{'); | 
 |   MaybeEmitNamedValue(builder, has_min_, "min", min_); | 
 |   MaybeEmitNamedValue(builder, has_max_, "max", max_); | 
 |   MaybeEmitNamedValue(builder, has_exact_, "exact", exact_); | 
 |   MaybeEmitNamedValue(builder, has_ideal_, "ideal", ideal_); | 
 |   builder.Append('}'); | 
 |   return builder.ToString(); | 
 | } | 
 |  | 
 | StringConstraint::StringConstraint(const char* name) | 
 |     : BaseConstraint(name), exact_(), ideal_() {} | 
 |  | 
 | bool StringConstraint::Matches(WebString value) const { | 
 |   if (exact_.empty()) { | 
 |     return true; | 
 |   } | 
 |   for (const auto& choice : exact_) { | 
 |     if (value == choice) { | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool StringConstraint::IsEmpty() const { | 
 |   return exact_.empty() && ideal_.empty(); | 
 | } | 
 |  | 
 | bool StringConstraint::HasMandatory() const { | 
 |   return !exact_.empty(); | 
 | } | 
 |  | 
 | const WebVector<WebString>& StringConstraint::Exact() const { | 
 |   return exact_; | 
 | } | 
 |  | 
 | const WebVector<WebString>& StringConstraint::Ideal() const { | 
 |   return ideal_; | 
 | } | 
 |  | 
 | WebString StringConstraint::ToString() const { | 
 |   StringBuilder builder; | 
 |   builder.Append('{'); | 
 |   if (!ideal_.empty()) { | 
 |     builder.Append("ideal: ["); | 
 |     bool first = true; | 
 |     for (const auto& iter : ideal_) { | 
 |       if (!first) | 
 |         builder.Append(", "); | 
 |       builder.Append('"'); | 
 |       builder.Append(iter); | 
 |       builder.Append('"'); | 
 |       first = false; | 
 |     } | 
 |     builder.Append(']'); | 
 |   } | 
 |   if (!exact_.empty()) { | 
 |     if (builder.length() > 1) | 
 |       builder.Append(", "); | 
 |     builder.Append("exact: ["); | 
 |     bool first = true; | 
 |     for (const auto& iter : exact_) { | 
 |       if (!first) | 
 |         builder.Append(", "); | 
 |       builder.Append('"'); | 
 |       builder.Append(iter); | 
 |       builder.Append('"'); | 
 |     } | 
 |     builder.Append(']'); | 
 |   } | 
 |   builder.Append('}'); | 
 |   return builder.ToString(); | 
 | } | 
 |  | 
 | BooleanConstraint::BooleanConstraint(const char* name) | 
 |     : BaseConstraint(name), | 
 |       ideal_(false), | 
 |       exact_(false), | 
 |       has_ideal_(false), | 
 |       has_exact_(false) {} | 
 |  | 
 | bool BooleanConstraint::Matches(bool value) const { | 
 |   if (has_exact_ && static_cast<bool>(exact_) != value) { | 
 |     return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool BooleanConstraint::IsEmpty() const { | 
 |   return !has_ideal_ && !has_exact_; | 
 | } | 
 |  | 
 | bool BooleanConstraint::HasMandatory() const { | 
 |   return has_exact_; | 
 | } | 
 |  | 
 | WebString BooleanConstraint::ToString() const { | 
 |   StringBuilder builder; | 
 |   builder.Append('{'); | 
 |   MaybeEmitNamedBoolean(builder, has_exact_, "exact", Exact()); | 
 |   MaybeEmitNamedBoolean(builder, has_ideal_, "ideal", Ideal()); | 
 |   builder.Append('}'); | 
 |   return builder.ToString(); | 
 | } | 
 |  | 
 | WebMediaTrackConstraintSet::WebMediaTrackConstraintSet() | 
 |     : width("width"), | 
 |       height("height"), | 
 |       aspect_ratio("aspectRatio"), | 
 |       frame_rate("frameRate"), | 
 |       facing_mode("facingMode"), | 
 |       volume("volume"), | 
 |       sample_rate("sampleRate"), | 
 |       sample_size("sampleSize"), | 
 |       echo_cancellation("echoCancellation"), | 
 |       latency("latency"), | 
 |       channel_count("channelCount"), | 
 |       device_id("deviceId"), | 
 |       disable_local_echo("disableLocalEcho"), | 
 |       group_id("groupId"), | 
 |       video_kind("videoKind"), | 
 |       depth_near("depthNear"), | 
 |       depth_far("depthFar"), | 
 |       focal_length_x("focalLengthX"), | 
 |       focal_length_y("focalLengthY"), | 
 |       media_stream_source("mediaStreamSource"), | 
 |       render_to_associated_sink("chromeRenderToAssociatedSink"), | 
 |       hotword_enabled("hotwordEnabled"), | 
 |       goog_echo_cancellation("googEchoCancellation"), | 
 |       goog_experimental_echo_cancellation("googExperimentalEchoCancellation"), | 
 |       goog_auto_gain_control("googAutoGainControl"), | 
 |       goog_experimental_auto_gain_control("googExperimentalAutoGainControl"), | 
 |       goog_noise_suppression("googNoiseSuppression"), | 
 |       goog_highpass_filter("googHighpassFilter"), | 
 |       goog_typing_noise_detection("googTypingNoiseDetection"), | 
 |       goog_experimental_noise_suppression("googExperimentalNoiseSuppression"), | 
 |       goog_beamforming("googBeamforming"), | 
 |       goog_array_geometry("googArrayGeometry"), | 
 |       goog_audio_mirroring("googAudioMirroring"), | 
 |       goog_da_echo_cancellation("googDAEchoCancellation"), | 
 |       goog_noise_reduction("googNoiseReduction"), | 
 |       offer_to_receive_audio("offerToReceiveAudio"), | 
 |       offer_to_receive_video("offerToReceiveVideo"), | 
 |       voice_activity_detection("voiceActivityDetection"), | 
 |       ice_restart("iceRestart"), | 
 |       goog_use_rtp_mux("googUseRtpMux"), | 
 |       enable_dtls_srtp("enableDtlsSrtp"), | 
 |       enable_rtp_data_channels("enableRtpDataChannels"), | 
 |       enable_dscp("enableDscp"), | 
 |       enable_i_pv6("enableIPv6"), | 
 |       goog_enable_video_suspend_below_min_bitrate( | 
 |           "googEnableVideoSuspendBelowMinBitrate"), | 
 |       goog_num_unsignalled_recv_streams("googNumUnsignalledRecvStreams"), | 
 |       goog_combined_audio_video_bwe("googCombinedAudioVideoBwe"), | 
 |       goog_screencast_min_bitrate("googScreencastMinBitrate"), | 
 |       goog_cpu_overuse_detection("googCpuOveruseDetection"), | 
 |       goog_cpu_underuse_threshold("googCpuUnderuseThreshold"), | 
 |       goog_cpu_overuse_threshold("googCpuOveruseThreshold"), | 
 |       goog_cpu_underuse_encode_rsd_threshold( | 
 |           "googCpuUnderuseEncodeRsdThreshold"), | 
 |       goog_cpu_overuse_encode_rsd_threshold("googCpuOveruseEncodeRsdThreshold"), | 
 |       goog_cpu_overuse_encode_usage("googCpuOveruseEncodeUsage"), | 
 |       goog_high_start_bitrate("googHighStartBitrate"), | 
 |       goog_payload_padding("googPayloadPadding"), | 
 |       goog_latency_ms("latencyMs"), | 
 |       goog_power_line_frequency("googPowerLineFrequency") {} | 
 |  | 
 | std::vector<const BaseConstraint*> WebMediaTrackConstraintSet::AllConstraints() | 
 |     const { | 
 |   const BaseConstraint* temp[] = {&width, | 
 |                                   &height, | 
 |                                   &aspect_ratio, | 
 |                                   &frame_rate, | 
 |                                   &facing_mode, | 
 |                                   &volume, | 
 |                                   &sample_rate, | 
 |                                   &sample_size, | 
 |                                   &echo_cancellation, | 
 |                                   &latency, | 
 |                                   &channel_count, | 
 |                                   &device_id, | 
 |                                   &group_id, | 
 |                                   &video_kind, | 
 |                                   &depth_near, | 
 |                                   &depth_far, | 
 |                                   &focal_length_x, | 
 |                                   &focal_length_y, | 
 |                                   &media_stream_source, | 
 |                                   &disable_local_echo, | 
 |                                   &render_to_associated_sink, | 
 |                                   &hotword_enabled, | 
 |                                   &goog_echo_cancellation, | 
 |                                   &goog_experimental_echo_cancellation, | 
 |                                   &goog_auto_gain_control, | 
 |                                   &goog_experimental_auto_gain_control, | 
 |                                   &goog_noise_suppression, | 
 |                                   &goog_highpass_filter, | 
 |                                   &goog_typing_noise_detection, | 
 |                                   &goog_experimental_noise_suppression, | 
 |                                   &goog_beamforming, | 
 |                                   &goog_array_geometry, | 
 |                                   &goog_audio_mirroring, | 
 |                                   &goog_da_echo_cancellation, | 
 |                                   &goog_noise_reduction, | 
 |                                   &offer_to_receive_audio, | 
 |                                   &offer_to_receive_video, | 
 |                                   &voice_activity_detection, | 
 |                                   &ice_restart, | 
 |                                   &goog_use_rtp_mux, | 
 |                                   &enable_dtls_srtp, | 
 |                                   &enable_rtp_data_channels, | 
 |                                   &enable_dscp, | 
 |                                   &enable_i_pv6, | 
 |                                   &goog_enable_video_suspend_below_min_bitrate, | 
 |                                   &goog_num_unsignalled_recv_streams, | 
 |                                   &goog_combined_audio_video_bwe, | 
 |                                   &goog_screencast_min_bitrate, | 
 |                                   &goog_cpu_overuse_detection, | 
 |                                   &goog_cpu_underuse_threshold, | 
 |                                   &goog_cpu_overuse_threshold, | 
 |                                   &goog_cpu_underuse_encode_rsd_threshold, | 
 |                                   &goog_cpu_overuse_encode_rsd_threshold, | 
 |                                   &goog_cpu_overuse_encode_usage, | 
 |                                   &goog_high_start_bitrate, | 
 |                                   &goog_payload_padding, | 
 |                                   &goog_latency_ms, | 
 |                                   &goog_power_line_frequency}; | 
 |   const int element_count = sizeof(temp) / sizeof(temp[0]); | 
 |   return std::vector<const BaseConstraint*>(&temp[0], &temp[element_count]); | 
 | } | 
 |  | 
 | bool WebMediaTrackConstraintSet::IsEmpty() const { | 
 |   for (const auto& constraint : AllConstraints()) { | 
 |     if (!constraint->IsEmpty()) | 
 |       return false; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool WebMediaTrackConstraintSet::HasMandatoryOutsideSet( | 
 |     const std::vector<std::string>& good_names, | 
 |     std::string& found_name) const { | 
 |   for (const auto& constraint : AllConstraints()) { | 
 |     if (constraint->HasMandatory()) { | 
 |       if (std::find(good_names.begin(), good_names.end(), | 
 |                     constraint->GetName()) == good_names.end()) { | 
 |         found_name = constraint->GetName(); | 
 |         return true; | 
 |       } | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool WebMediaTrackConstraintSet::HasMandatory() const { | 
 |   std::string dummy_string; | 
 |   return HasMandatoryOutsideSet(std::vector<std::string>(), dummy_string); | 
 | } | 
 |  | 
 | WebString WebMediaTrackConstraintSet::ToString() const { | 
 |   StringBuilder builder; | 
 |   bool first = true; | 
 |   for (const auto& constraint : AllConstraints()) { | 
 |     if (!constraint->IsEmpty()) { | 
 |       if (!first) | 
 |         builder.Append(", "); | 
 |       builder.Append(constraint->GetName()); | 
 |       builder.Append(": "); | 
 |       builder.Append(constraint->ToString()); | 
 |       first = false; | 
 |     } | 
 |   } | 
 |   return builder.ToString(); | 
 | } | 
 |  | 
 | // WebMediaConstraints | 
 |  | 
 | void WebMediaConstraints::Assign(const WebMediaConstraints& other) { | 
 |   private_ = other.private_; | 
 | } | 
 |  | 
 | void WebMediaConstraints::Reset() { | 
 |   private_.Reset(); | 
 | } | 
 |  | 
 | bool WebMediaConstraints::IsEmpty() const { | 
 |   return private_.IsNull() || private_->IsEmpty(); | 
 | } | 
 |  | 
 | void WebMediaConstraints::Initialize() { | 
 |   DCHECK(IsNull()); | 
 |   private_ = WebMediaConstraintsPrivate::Create(); | 
 | } | 
 |  | 
 | void WebMediaConstraints::Initialize( | 
 |     const WebMediaTrackConstraintSet& basic, | 
 |     const WebVector<WebMediaTrackConstraintSet>& advanced) { | 
 |   DCHECK(IsNull()); | 
 |   private_ = WebMediaConstraintsPrivate::Create(basic, advanced); | 
 | } | 
 |  | 
 | const WebMediaTrackConstraintSet& WebMediaConstraints::Basic() const { | 
 |   DCHECK(!IsNull()); | 
 |   return private_->Basic(); | 
 | } | 
 |  | 
 | const WebVector<WebMediaTrackConstraintSet>& WebMediaConstraints::Advanced() | 
 |     const { | 
 |   DCHECK(!IsNull()); | 
 |   return private_->Advanced(); | 
 | } | 
 |  | 
 | const WebString WebMediaConstraints::ToString() const { | 
 |   if (IsNull()) | 
 |     return WebString(""); | 
 |   return private_->ToString(); | 
 | } | 
 |  | 
 | }  // namespace blink |