| /* |
| * 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 "third_party/blink/public/platform/web_media_constraints.h" |
| |
| #include <math.h> |
| #include "base/memory/scoped_refptr.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" |
| #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" |
| #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.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 |
| |
| const char kEchoCancellationTypeBrowser[] = "browser"; |
| const char kEchoCancellationTypeAec3[] = "aec3"; |
| const char kEchoCancellationTypeSystem[] = "system"; |
| |
| 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; |
| |
| bool BaseConstraint::HasMandatory() const { |
| return HasMin() || HasMax() || HasExact(); |
| } |
| |
| 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(int32_t 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_; |
| } |
| |
| 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_; |
| } |
| |
| 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(); |
| } |
| |
| 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_; |
| } |
| |
| 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"), |
| echo_cancellation_type("echoCancellationType"), |
| 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_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") {} |
| |
| 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, |
| &echo_cancellation_type, |
| &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_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}; |
| const int element_count = sizeof(temp) / sizeof(temp[0]); |
| return std::vector<const BaseConstraint*>(&temp[0], &temp[element_count]); |
| } |
| |
| bool WebMediaTrackConstraintSet::IsEmpty() const { |
| for (auto* const 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 (auto* const 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); |
| } |
| |
| bool WebMediaTrackConstraintSet::HasMin() const { |
| for (auto* const constraint : AllConstraints()) { |
| if (constraint->HasMin()) |
| return true; |
| } |
| return false; |
| } |
| |
| bool WebMediaTrackConstraintSet::HasExact() const { |
| for (auto* const constraint : AllConstraints()) { |
| if (constraint->HasExact()) |
| return true; |
| } |
| return false; |
| } |
| |
| WebString WebMediaTrackConstraintSet::ToString() const { |
| StringBuilder builder; |
| bool first = true; |
| for (auto* const 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 |