| 1 | /* |
| 2 | * Copyright (C) 2012 Google Inc. All rights reserved. |
| 3 | * Copyright (C) 2016 Apple Inc. All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * |
| 9 | * 1. Redistributions of source code must retain the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer. |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer |
| 13 | * in the documentation and/or other materials provided with the |
| 14 | * distribution. |
| 15 | * 3. Neither the name of Google Inc. nor the names of its contributors |
| 16 | * may be used to endorse or promote products derived from this |
| 17 | * software without specific prior written permission. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | #pragma once |
| 33 | |
| 34 | #if ENABLE(MEDIA_STREAM) |
| 35 | |
| 36 | #include "RealtimeMediaSourceSupportedConstraints.h" |
| 37 | #include <cstdlib> |
| 38 | #include <wtf/Function.h> |
| 39 | #include <wtf/Vector.h> |
| 40 | |
| 41 | namespace WebCore { |
| 42 | |
| 43 | class MediaConstraint { |
| 44 | public: |
| 45 | enum class DataType { None, Integer, Double, Boolean, String }; |
| 46 | |
| 47 | bool isInt() const { return m_dataType == DataType::Integer; } |
| 48 | bool isDouble() const { return m_dataType == DataType::Double; } |
| 49 | bool isBoolean() const { return m_dataType == DataType::Boolean; } |
| 50 | bool isString() const { return m_dataType == DataType::String; } |
| 51 | |
| 52 | DataType dataType() const { return m_dataType; } |
| 53 | MediaConstraintType constraintType() const { return m_constraintType; } |
| 54 | const String& name() const { return m_name; } |
| 55 | |
| 56 | template <class Encoder> void encode(Encoder& encoder) const |
| 57 | { |
| 58 | encoder.encodeEnum(m_constraintType); |
| 59 | encoder << m_name; |
| 60 | encoder.encodeEnum(m_dataType); |
| 61 | } |
| 62 | |
| 63 | template <class Decoder> static bool decode(Decoder& decoder, MediaConstraint& constraint) |
| 64 | { |
| 65 | if (!decoder.decodeEnum(constraint.m_constraintType)) |
| 66 | return false; |
| 67 | |
| 68 | if (!decoder.decode(constraint.m_name)) |
| 69 | return false; |
| 70 | |
| 71 | if (!decoder.decodeEnum(constraint.m_dataType)) |
| 72 | return false; |
| 73 | |
| 74 | return true; |
| 75 | } |
| 76 | |
| 77 | protected: |
| 78 | MediaConstraint(const String& name, MediaConstraintType constraintType, DataType dataType) |
| 79 | : m_name(name) |
| 80 | , m_constraintType(constraintType) |
| 81 | , m_dataType(dataType) |
| 82 | { |
| 83 | } |
| 84 | |
| 85 | MediaConstraint() = default; |
| 86 | ~MediaConstraint() = default; |
| 87 | |
| 88 | private: |
| 89 | String m_name; |
| 90 | MediaConstraintType m_constraintType { MediaConstraintType::Unknown }; |
| 91 | DataType m_dataType { DataType::None }; |
| 92 | }; |
| 93 | |
| 94 | template<class ValueType> |
| 95 | class NumericConstraint : public MediaConstraint { |
| 96 | public: |
| 97 | void setMin(ValueType value) { m_min = value; } |
| 98 | void setMax(ValueType value) { m_max = value; } |
| 99 | void setExact(ValueType value) { m_exact = value; } |
| 100 | void setIdeal(ValueType value) { m_ideal = value; } |
| 101 | |
| 102 | bool getMin(ValueType& min) const |
| 103 | { |
| 104 | if (!m_min) |
| 105 | return false; |
| 106 | |
| 107 | min = m_min.value(); |
| 108 | return true; |
| 109 | } |
| 110 | |
| 111 | bool getMax(ValueType& max) const |
| 112 | { |
| 113 | if (!m_max) |
| 114 | return false; |
| 115 | |
| 116 | max = m_max.value(); |
| 117 | return true; |
| 118 | } |
| 119 | |
| 120 | bool getExact(ValueType& exact) const |
| 121 | { |
| 122 | if (!m_exact) |
| 123 | return false; |
| 124 | |
| 125 | exact = m_exact.value(); |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | bool getIdeal(ValueType& ideal) const |
| 130 | { |
| 131 | if (!m_ideal) |
| 132 | return false; |
| 133 | |
| 134 | ideal = m_ideal.value(); |
| 135 | return true; |
| 136 | } |
| 137 | |
| 138 | bool nearlyEqual(double a, double b) const |
| 139 | { |
| 140 | // Don't require strict equality when comparing constraints, or many floating point constraint values, |
| 141 | // e.g. "aspectRatio: 1.333", will never match. |
| 142 | const double epsilon = 0.00001; |
| 143 | return std::abs(a - b) <= epsilon; |
| 144 | } |
| 145 | |
| 146 | double fitnessDistance(ValueType rangeMin, ValueType rangeMax) const |
| 147 | { |
| 148 | // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints |
| 149 | // 1. If the constraint is not supported by the browser, the fitness distance is 0. |
| 150 | if (isEmpty()) |
| 151 | return 0; |
| 152 | |
| 153 | // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings |
| 154 | // dictionary's value for the constraint does not satisfy the constraint, the |
| 155 | // fitness distance is positive infinity. |
| 156 | bool valid = validForRange(rangeMin, rangeMax); |
| 157 | if (m_exact) { |
| 158 | if (valid && m_min && m_exact.value() < m_min.value()) |
| 159 | valid = false; |
| 160 | if (valid && m_max && m_exact.value() > m_max.value()) |
| 161 | valid = false; |
| 162 | if (!valid) |
| 163 | return std::numeric_limits<double>::infinity(); |
| 164 | } |
| 165 | |
| 166 | if (m_min) { |
| 167 | if (valid && m_max && m_min.value() > m_max.value()) |
| 168 | valid = false; |
| 169 | if (!valid) |
| 170 | return std::numeric_limits<double>::infinity(); |
| 171 | } |
| 172 | |
| 173 | if (m_max) { |
| 174 | if (valid && m_min && m_max.value() < m_min.value()) |
| 175 | valid = false; |
| 176 | if (!valid) |
| 177 | return std::numeric_limits<double>::infinity(); |
| 178 | } |
| 179 | |
| 180 | // 3. If no ideal value is specified, the fitness distance is 0. |
| 181 | if (!m_ideal) |
| 182 | return 0; |
| 183 | |
| 184 | // 4. For all positive numeric non-required constraints (such as height, width, frameRate, |
| 185 | // aspectRatio, sampleRate and sampleSize), the fitness distance is the result of the formula |
| 186 | // |
| 187 | // (actual == ideal) ? 0 : |actual - ideal| / max(|actual|,|ideal|) |
| 188 | ValueType ideal = m_ideal.value(); |
| 189 | if (ideal >= rangeMin && ideal <= rangeMax) |
| 190 | return 0; |
| 191 | |
| 192 | ideal = ideal > std::max(rangeMin, rangeMax) ? rangeMax : rangeMin; |
| 193 | return static_cast<double>(std::abs(ideal - m_ideal.value())) / std::max(std::abs(ideal), std::abs(m_ideal.value())); |
| 194 | } |
| 195 | |
| 196 | double fitnessDistance(const Vector<ValueType>& discreteCapabilityValues) const |
| 197 | { |
| 198 | double minDistance = std::numeric_limits<double>::infinity(); |
| 199 | |
| 200 | for (auto& value : discreteCapabilityValues) { |
| 201 | auto distance = fitnessDistance(value, value); |
| 202 | if (distance < minDistance) |
| 203 | minDistance = distance; |
| 204 | } |
| 205 | |
| 206 | return minDistance; |
| 207 | } |
| 208 | |
| 209 | bool validForRange(ValueType rangeMin, ValueType rangeMax) const |
| 210 | { |
| 211 | if (isEmpty()) |
| 212 | return false; |
| 213 | |
| 214 | if (m_exact) { |
| 215 | const ValueType exact = m_exact.value(); |
| 216 | if (exact < rangeMin && !nearlyEqual(exact, rangeMin)) |
| 217 | return false; |
| 218 | if (exact > rangeMax && !nearlyEqual(exact, rangeMax)) |
| 219 | return false; |
| 220 | } |
| 221 | |
| 222 | if (m_min) { |
| 223 | const ValueType constraintMin = m_min.value(); |
| 224 | if (constraintMin > rangeMax && !nearlyEqual(constraintMin, rangeMax)) |
| 225 | return false; |
| 226 | } |
| 227 | |
| 228 | if (m_max) { |
| 229 | const ValueType constraintMax = m_max.value(); |
| 230 | if (constraintMax < rangeMin && !nearlyEqual(constraintMax, rangeMin)) |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | return true; |
| 235 | } |
| 236 | |
| 237 | ValueType find(const WTF::Function<bool(ValueType)>& function) const |
| 238 | { |
| 239 | if (m_min && function(m_min.value())) |
| 240 | return m_min.value(); |
| 241 | |
| 242 | if (m_max && function(m_max.value())) |
| 243 | return m_max.value(); |
| 244 | |
| 245 | if (m_exact && function(m_exact.value())) |
| 246 | return m_exact.value(); |
| 247 | |
| 248 | if (m_ideal && function(m_ideal.value())) |
| 249 | return m_ideal.value(); |
| 250 | |
| 251 | return 0; |
| 252 | } |
| 253 | |
| 254 | ValueType valueForCapabilityRange(ValueType current, ValueType capabilityMin, ValueType capabilityMax) const |
| 255 | { |
| 256 | ValueType value { 0 }; |
| 257 | ValueType min { capabilityMin }; |
| 258 | ValueType max { capabilityMax }; |
| 259 | |
| 260 | if (m_exact) { |
| 261 | ASSERT(validForRange(capabilityMin, capabilityMax)); |
| 262 | return m_exact.value(); |
| 263 | } |
| 264 | |
| 265 | if (m_min) { |
| 266 | value = m_min.value(); |
| 267 | ASSERT(validForRange(value, capabilityMax)); |
| 268 | if (value > min) |
| 269 | min = value; |
| 270 | if (value < min) |
| 271 | value = min; |
| 272 | |
| 273 | // If there is no ideal, don't change if minimum is smaller than current. |
| 274 | if (!m_ideal && value < current) |
| 275 | value = current; |
| 276 | } |
| 277 | |
| 278 | if (m_max) { |
| 279 | value = m_max.value(); |
| 280 | ASSERT(validForRange(capabilityMin, value)); |
| 281 | if (value < max) |
| 282 | max = value; |
| 283 | if (value > max) |
| 284 | value = max; |
| 285 | } |
| 286 | |
| 287 | if (m_ideal) |
| 288 | value = std::max(min, std::min(max, m_ideal.value())); |
| 289 | |
| 290 | return value; |
| 291 | } |
| 292 | |
| 293 | ValueType valueForDiscreteCapabilityValues(ValueType current, const Vector<ValueType>& discreteCapabilityValues) const |
| 294 | { |
| 295 | ValueType value { 0 }; |
| 296 | Optional<ValueType> min; |
| 297 | Optional<ValueType> max; |
| 298 | |
| 299 | if (m_exact) { |
| 300 | ASSERT(discreteCapabilityValues.contains(m_exact.value())); |
| 301 | return m_exact.value(); |
| 302 | } |
| 303 | |
| 304 | if (m_min) { |
| 305 | auto index = discreteCapabilityValues.findMatching([&](ValueType value) { return m_min.value() >= value; }); |
| 306 | if (index != notFound) { |
| 307 | min = value = discreteCapabilityValues[index]; |
| 308 | |
| 309 | // If there is no ideal, don't change if minimum is smaller than current. |
| 310 | if (!m_ideal && value < current) |
| 311 | value = current; |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | if (m_max && m_max.value() >= discreteCapabilityValues[0]) { |
| 316 | for (auto& discreteValue : discreteCapabilityValues) { |
| 317 | if (m_max.value() <= discreteValue) |
| 318 | max = value = discreteValue; |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | if (m_ideal && discreteCapabilityValues.contains(m_ideal.value())) { |
| 323 | value = m_ideal.value(); |
| 324 | if (max) |
| 325 | value = std::min(max.value(), value); |
| 326 | if (min) |
| 327 | value = std::max(min.value(), value); |
| 328 | } |
| 329 | |
| 330 | return value; |
| 331 | } |
| 332 | |
| 333 | bool isEmpty() const { return !m_min && !m_max && !m_exact && !m_ideal; } |
| 334 | bool isMandatory() const { return m_min || m_max || m_exact; } |
| 335 | |
| 336 | template <class Encoder> void encode(Encoder& encoder) const |
| 337 | { |
| 338 | MediaConstraint::encode(encoder); |
| 339 | |
| 340 | encoder << m_min; |
| 341 | encoder << m_max; |
| 342 | encoder << m_exact; |
| 343 | encoder << m_ideal; |
| 344 | } |
| 345 | |
| 346 | template <class Decoder> static bool decode(Decoder& decoder, NumericConstraint& constraint) |
| 347 | { |
| 348 | if (!MediaConstraint::decode(decoder, constraint)) |
| 349 | return false; |
| 350 | |
| 351 | if (!decoder.decode(constraint.m_min)) |
| 352 | return false; |
| 353 | if (!decoder.decode(constraint.m_max)) |
| 354 | return false; |
| 355 | if (!decoder.decode(constraint.m_exact)) |
| 356 | return false; |
| 357 | if (!decoder.decode(constraint.m_ideal)) |
| 358 | return false; |
| 359 | |
| 360 | return true; |
| 361 | } |
| 362 | |
| 363 | protected: |
| 364 | NumericConstraint(const String& name, MediaConstraintType type, DataType dataType) |
| 365 | : MediaConstraint(name, type, dataType) |
| 366 | { |
| 367 | } |
| 368 | |
| 369 | NumericConstraint() = default; |
| 370 | |
| 371 | void innerMerge(const NumericConstraint& other) |
| 372 | { |
| 373 | if (other.isEmpty()) |
| 374 | return; |
| 375 | |
| 376 | ValueType value; |
| 377 | if (other.getExact(value)) |
| 378 | m_exact = value; |
| 379 | |
| 380 | if (other.getMin(value)) |
| 381 | m_min = value; |
| 382 | |
| 383 | if (other.getMax(value)) |
| 384 | m_max = value; |
| 385 | |
| 386 | // https://w3c.github.io/mediacapture-main/#constrainable-interface |
| 387 | // When processing advanced constraints: |
| 388 | // ... the User Agent must attempt to apply, individually, any 'ideal' constraints or |
| 389 | // a constraint given as a bare value for the property. Of these properties, it must |
| 390 | // satisfy the largest number that it can, in any order. |
| 391 | if (other.getIdeal(value)) { |
| 392 | if (!m_ideal || value > m_ideal.value()) |
| 393 | m_ideal = value; |
| 394 | } |
| 395 | } |
| 396 | |
| 397 | Optional<ValueType> m_min; |
| 398 | Optional<ValueType> m_max; |
| 399 | Optional<ValueType> m_exact; |
| 400 | Optional<ValueType> m_ideal; |
| 401 | }; |
| 402 | |
| 403 | class IntConstraint final : public NumericConstraint<int> { |
| 404 | public: |
| 405 | IntConstraint(const String& name, MediaConstraintType type) |
| 406 | : NumericConstraint<int>(name, type, DataType::Integer) |
| 407 | { |
| 408 | } |
| 409 | |
| 410 | IntConstraint() = default; |
| 411 | |
| 412 | void merge(const MediaConstraint& other) |
| 413 | { |
| 414 | ASSERT(other.isInt()); |
| 415 | NumericConstraint::innerMerge(downcast<const IntConstraint>(other)); |
| 416 | } |
| 417 | }; |
| 418 | |
| 419 | class DoubleConstraint final : public NumericConstraint<double> { |
| 420 | public: |
| 421 | DoubleConstraint(const String& name, MediaConstraintType type) |
| 422 | : NumericConstraint<double>(name, type, DataType::Double) |
| 423 | { |
| 424 | } |
| 425 | |
| 426 | DoubleConstraint() = default; |
| 427 | |
| 428 | void merge(const MediaConstraint& other) |
| 429 | { |
| 430 | ASSERT(other.isDouble()); |
| 431 | NumericConstraint::innerMerge(downcast<DoubleConstraint>(other)); |
| 432 | } |
| 433 | }; |
| 434 | |
| 435 | class BooleanConstraint final : public MediaConstraint { |
| 436 | public: |
| 437 | BooleanConstraint(const String& name, MediaConstraintType type) |
| 438 | : MediaConstraint(name, type, DataType::Boolean) |
| 439 | { |
| 440 | } |
| 441 | |
| 442 | BooleanConstraint() = default; |
| 443 | |
| 444 | void setExact(bool value) { m_exact = value; } |
| 445 | void setIdeal(bool value) { m_ideal = value; } |
| 446 | |
| 447 | bool getExact(bool& exact) const |
| 448 | { |
| 449 | if (!m_exact) |
| 450 | return false; |
| 451 | |
| 452 | exact = m_exact.value(); |
| 453 | return true; |
| 454 | } |
| 455 | |
| 456 | bool getIdeal(bool& ideal) const |
| 457 | { |
| 458 | if (!m_ideal) |
| 459 | return false; |
| 460 | |
| 461 | ideal = m_ideal.value(); |
| 462 | return true; |
| 463 | } |
| 464 | |
| 465 | double fitnessDistance(bool value) const |
| 466 | { |
| 467 | // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints |
| 468 | // 1. If the constraint is not supported by the browser, the fitness distance is 0. |
| 469 | if (isEmpty()) |
| 470 | return 0; |
| 471 | |
| 472 | // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings |
| 473 | // dictionary's value for the constraint does not satisfy the constraint, the |
| 474 | // fitness distance is positive infinity. |
| 475 | if (m_exact && value != m_exact.value()) |
| 476 | return std::numeric_limits<double>::infinity(); |
| 477 | |
| 478 | // 3. If no ideal value is specified, the fitness distance is 0. |
| 479 | if (!m_ideal || m_ideal.value() == value) |
| 480 | return 0; |
| 481 | |
| 482 | // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode, |
| 483 | // echoCancellation), the fitness distance is the result of the formula |
| 484 | // (actual == ideal) ? 0 : 1 |
| 485 | return 1; |
| 486 | } |
| 487 | |
| 488 | void merge(const MediaConstraint& other) |
| 489 | { |
| 490 | ASSERT(other.isBoolean()); |
| 491 | const BooleanConstraint& typedOther = downcast<BooleanConstraint>(other); |
| 492 | |
| 493 | if (typedOther.isEmpty()) |
| 494 | return; |
| 495 | |
| 496 | bool value; |
| 497 | if (typedOther.getExact(value)) |
| 498 | m_exact = value; |
| 499 | |
| 500 | if (typedOther.getIdeal(value)) { |
| 501 | if (!m_ideal || (value && !m_ideal.value())) |
| 502 | m_ideal = value; |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | bool isEmpty() const { return !m_exact && !m_ideal; }; |
| 507 | bool isMandatory() const { return bool(m_exact); } |
| 508 | |
| 509 | template <class Encoder> void encode(Encoder& encoder) const |
| 510 | { |
| 511 | MediaConstraint::encode(encoder); |
| 512 | encoder << m_exact; |
| 513 | encoder << m_ideal; |
| 514 | } |
| 515 | |
| 516 | template <class Decoder> static bool decode(Decoder& decoder, BooleanConstraint& constraint) |
| 517 | { |
| 518 | if (!MediaConstraint::decode(decoder, constraint)) |
| 519 | return false; |
| 520 | |
| 521 | if (!decoder.decode(constraint.m_exact)) |
| 522 | return false; |
| 523 | if (!decoder.decode(constraint.m_ideal)) |
| 524 | return false; |
| 525 | |
| 526 | return true; |
| 527 | } |
| 528 | |
| 529 | private: |
| 530 | Optional<bool> m_exact; |
| 531 | Optional<bool> m_ideal; |
| 532 | }; |
| 533 | |
| 534 | class StringConstraint : public MediaConstraint { |
| 535 | public: |
| 536 | StringConstraint(const String& name, MediaConstraintType type) |
| 537 | : MediaConstraint(name, type, DataType::String) |
| 538 | { |
| 539 | } |
| 540 | |
| 541 | StringConstraint() = default; |
| 542 | |
| 543 | void setExact(const String& value) |
| 544 | { |
| 545 | m_exact.clear(); |
| 546 | m_exact.append(value); |
| 547 | } |
| 548 | |
| 549 | void appendExact(const String& value) |
| 550 | { |
| 551 | m_exact.append(value); |
| 552 | } |
| 553 | |
| 554 | void setIdeal(const String& value) |
| 555 | { |
| 556 | m_ideal.clear(); |
| 557 | m_ideal.append(value); |
| 558 | } |
| 559 | |
| 560 | void appendIdeal(const String& value) |
| 561 | { |
| 562 | m_ideal.append(value); |
| 563 | } |
| 564 | |
| 565 | bool getExact(Vector<String>& exact) const |
| 566 | { |
| 567 | if (!m_exact.isEmpty()) |
| 568 | return false; |
| 569 | |
| 570 | exact = m_exact; |
| 571 | return true; |
| 572 | } |
| 573 | |
| 574 | bool getIdeal(Vector<String>& ideal) const |
| 575 | { |
| 576 | if (!m_ideal.isEmpty()) |
| 577 | return false; |
| 578 | |
| 579 | ideal = m_ideal; |
| 580 | return true; |
| 581 | } |
| 582 | |
| 583 | double fitnessDistance(const String&) const; |
| 584 | double fitnessDistance(const Vector<String>&) const; |
| 585 | |
| 586 | const String& find(const WTF::Function<bool(const String&)>&) const; |
| 587 | |
| 588 | bool isEmpty() const { return m_exact.isEmpty() && m_ideal.isEmpty(); } |
| 589 | bool isMandatory() const { return !m_exact.isEmpty(); } |
| 590 | WEBCORE_EXPORT void merge(const MediaConstraint&); |
| 591 | |
| 592 | template <class Encoder> void encode(Encoder& encoder) const |
| 593 | { |
| 594 | MediaConstraint::encode(encoder); |
| 595 | |
| 596 | encoder << m_exact; |
| 597 | encoder << m_ideal; |
| 598 | } |
| 599 | |
| 600 | template <class Decoder> static bool decode(Decoder& decoder, StringConstraint& constraint) |
| 601 | { |
| 602 | if (!MediaConstraint::decode(decoder, constraint)) |
| 603 | return false; |
| 604 | |
| 605 | if (!decoder.decode(constraint.m_exact)) |
| 606 | return false; |
| 607 | if (!decoder.decode(constraint.m_ideal)) |
| 608 | return false; |
| 609 | |
| 610 | return true; |
| 611 | } |
| 612 | |
| 613 | void removeEmptyStringConstraint() |
| 614 | { |
| 615 | m_exact.removeAllMatching([](auto& constraint) { |
| 616 | return constraint.isEmpty(); |
| 617 | }); |
| 618 | m_ideal.removeAllMatching([](auto& constraint) { |
| 619 | return constraint.isEmpty(); |
| 620 | }); |
| 621 | } |
| 622 | |
| 623 | private: |
| 624 | Vector<String> m_exact; |
| 625 | Vector<String> m_ideal; |
| 626 | }; |
| 627 | |
| 628 | class UnknownConstraint final : public MediaConstraint { |
| 629 | public: |
| 630 | UnknownConstraint(const String& name, MediaConstraintType type) |
| 631 | : MediaConstraint(name, type, DataType::None) |
| 632 | { |
| 633 | } |
| 634 | |
| 635 | private: |
| 636 | bool isEmpty() const { return true; } |
| 637 | bool isMandatory() const { return false; } |
| 638 | void merge(const MediaConstraint&) { } |
| 639 | }; |
| 640 | |
| 641 | class MediaTrackConstraintSetMap { |
| 642 | public: |
| 643 | WEBCORE_EXPORT void forEach(WTF::Function<void(const MediaConstraint&)>&&) const; |
| 644 | void filter(const WTF::Function<bool(const MediaConstraint&)>&) const; |
| 645 | bool isEmpty() const; |
| 646 | WEBCORE_EXPORT size_t size() const; |
| 647 | |
| 648 | WEBCORE_EXPORT void set(MediaConstraintType, Optional<IntConstraint>&&); |
| 649 | WEBCORE_EXPORT void set(MediaConstraintType, Optional<DoubleConstraint>&&); |
| 650 | WEBCORE_EXPORT void set(MediaConstraintType, Optional<BooleanConstraint>&&); |
| 651 | WEBCORE_EXPORT void set(MediaConstraintType, Optional<StringConstraint>&&); |
| 652 | |
| 653 | Optional<IntConstraint> width() const { return m_width; } |
| 654 | Optional<IntConstraint> height() const { return m_height; } |
| 655 | Optional<IntConstraint> sampleRate() const { return m_sampleRate; } |
| 656 | Optional<IntConstraint> sampleSize() const { return m_sampleSize; } |
| 657 | |
| 658 | Optional<DoubleConstraint> aspectRatio() const { return m_aspectRatio; } |
| 659 | Optional<DoubleConstraint> frameRate() const { return m_frameRate; } |
| 660 | Optional<DoubleConstraint> volume() const { return m_volume; } |
| 661 | |
| 662 | Optional<BooleanConstraint> echoCancellation() const { return m_echoCancellation; } |
| 663 | Optional<BooleanConstraint> displaySurface() const { return m_displaySurface; } |
| 664 | Optional<BooleanConstraint> logicalSurface() const { return m_logicalSurface; } |
| 665 | |
| 666 | Optional<StringConstraint> facingMode() const { return m_facingMode; } |
| 667 | Optional<StringConstraint> deviceId() const { return m_deviceId; } |
| 668 | Optional<StringConstraint> groupId() const { return m_groupId; } |
| 669 | |
| 670 | template <class Encoder> void encode(Encoder& encoder) const |
| 671 | { |
| 672 | encoder << m_width; |
| 673 | encoder << m_height; |
| 674 | encoder << m_sampleRate; |
| 675 | encoder << m_sampleSize; |
| 676 | |
| 677 | encoder << m_aspectRatio; |
| 678 | encoder << m_frameRate; |
| 679 | encoder << m_volume; |
| 680 | |
| 681 | encoder << m_echoCancellation; |
| 682 | encoder << m_displaySurface; |
| 683 | encoder << m_logicalSurface; |
| 684 | |
| 685 | encoder << m_facingMode; |
| 686 | encoder << m_deviceId; |
| 687 | encoder << m_groupId; |
| 688 | } |
| 689 | |
| 690 | template <class Decoder> static Optional<MediaTrackConstraintSetMap> decode(Decoder& decoder) |
| 691 | { |
| 692 | MediaTrackConstraintSetMap map; |
| 693 | if (!decoder.decode(map.m_width)) |
| 694 | return WTF::nullopt; |
| 695 | if (!decoder.decode(map.m_height)) |
| 696 | return WTF::nullopt; |
| 697 | if (!decoder.decode(map.m_sampleRate)) |
| 698 | return WTF::nullopt; |
| 699 | if (!decoder.decode(map.m_sampleSize)) |
| 700 | return WTF::nullopt; |
| 701 | |
| 702 | if (!decoder.decode(map.m_aspectRatio)) |
| 703 | return WTF::nullopt; |
| 704 | if (!decoder.decode(map.m_frameRate)) |
| 705 | return WTF::nullopt; |
| 706 | if (!decoder.decode(map.m_volume)) |
| 707 | return WTF::nullopt; |
| 708 | |
| 709 | if (!decoder.decode(map.m_echoCancellation)) |
| 710 | return WTF::nullopt; |
| 711 | if (!decoder.decode(map.m_displaySurface)) |
| 712 | return WTF::nullopt; |
| 713 | if (!decoder.decode(map.m_logicalSurface)) |
| 714 | return WTF::nullopt; |
| 715 | |
| 716 | if (!decoder.decode(map.m_facingMode)) |
| 717 | return WTF::nullopt; |
| 718 | if (!decoder.decode(map.m_deviceId)) |
| 719 | return WTF::nullopt; |
| 720 | if (!decoder.decode(map.m_groupId)) |
| 721 | return WTF::nullopt; |
| 722 | |
| 723 | return map; |
| 724 | } |
| 725 | |
| 726 | private: |
| 727 | Optional<IntConstraint> m_width; |
| 728 | Optional<IntConstraint> m_height; |
| 729 | Optional<IntConstraint> m_sampleRate; |
| 730 | Optional<IntConstraint> m_sampleSize; |
| 731 | |
| 732 | Optional<DoubleConstraint> m_aspectRatio; |
| 733 | Optional<DoubleConstraint> m_frameRate; |
| 734 | Optional<DoubleConstraint> m_volume; |
| 735 | |
| 736 | Optional<BooleanConstraint> m_echoCancellation; |
| 737 | Optional<BooleanConstraint> m_displaySurface; |
| 738 | Optional<BooleanConstraint> m_logicalSurface; |
| 739 | |
| 740 | Optional<StringConstraint> m_facingMode; |
| 741 | Optional<StringConstraint> m_deviceId; |
| 742 | Optional<StringConstraint> m_groupId; |
| 743 | }; |
| 744 | |
| 745 | class FlattenedConstraint { |
| 746 | public: |
| 747 | |
| 748 | void set(const MediaConstraint&); |
| 749 | void merge(const MediaConstraint&); |
| 750 | void append(const MediaConstraint&); |
| 751 | const MediaConstraint* find(MediaConstraintType) const; |
| 752 | bool isEmpty() const { return m_variants.isEmpty(); } |
| 753 | |
| 754 | class iterator { |
| 755 | public: |
| 756 | iterator(const FlattenedConstraint* constraint, size_t index) |
| 757 | : m_constraint(constraint) |
| 758 | , m_index(index) |
| 759 | #ifndef NDEBUG |
| 760 | , m_generation(constraint->m_generation) |
| 761 | #endif |
| 762 | { |
| 763 | } |
| 764 | |
| 765 | MediaConstraint& operator*() const |
| 766 | { |
| 767 | return m_constraint->m_variants.at(m_index).constraint(); |
| 768 | } |
| 769 | |
| 770 | iterator& operator++() |
| 771 | { |
| 772 | #ifndef NDEBUG |
| 773 | ASSERT(m_generation == m_constraint->m_generation); |
| 774 | #endif |
| 775 | m_index++; |
| 776 | return *this; |
| 777 | } |
| 778 | |
| 779 | bool operator==(const iterator& other) const { return m_index == other.m_index; } |
| 780 | bool operator!=(const iterator& other) const { return !(*this == other); } |
| 781 | |
| 782 | private: |
| 783 | const FlattenedConstraint* m_constraint { nullptr }; |
| 784 | size_t m_index { 0 }; |
| 785 | #ifndef NDEBUG |
| 786 | int m_generation { 0 }; |
| 787 | #endif |
| 788 | }; |
| 789 | |
| 790 | const iterator begin() const { return iterator(this, 0); } |
| 791 | const iterator end() const { return iterator(this, m_variants.size()); } |
| 792 | |
| 793 | private: |
| 794 | class ConstraintHolder { |
| 795 | public: |
| 796 | static ConstraintHolder create(const MediaConstraint& value) { return ConstraintHolder(value); } |
| 797 | |
| 798 | ~ConstraintHolder() |
| 799 | { |
| 800 | if (m_value.asRaw) { |
| 801 | switch (dataType()) { |
| 802 | case MediaConstraint::DataType::Integer: |
| 803 | delete m_value.asInteger; |
| 804 | break; |
| 805 | case MediaConstraint::DataType::Double: |
| 806 | delete m_value.asDouble; |
| 807 | break; |
| 808 | case MediaConstraint::DataType::Boolean: |
| 809 | delete m_value.asBoolean; |
| 810 | break; |
| 811 | case MediaConstraint::DataType::String: |
| 812 | delete m_value.asString; |
| 813 | break; |
| 814 | case MediaConstraint::DataType::None: |
| 815 | ASSERT_NOT_REACHED(); |
| 816 | break; |
| 817 | } |
| 818 | } |
| 819 | #ifndef NDEBUG |
| 820 | m_value.asRaw = reinterpret_cast<MediaConstraint*>(0xbbadbeef); |
| 821 | #endif |
| 822 | } |
| 823 | |
| 824 | ConstraintHolder(ConstraintHolder&& other) |
| 825 | { |
| 826 | switch (other.dataType()) { |
| 827 | case MediaConstraint::DataType::Integer: |
| 828 | m_value.asInteger = std::exchange(other.m_value.asInteger, nullptr); |
| 829 | break; |
| 830 | case MediaConstraint::DataType::Double: |
| 831 | m_value.asDouble = std::exchange(other.m_value.asDouble, nullptr); |
| 832 | break; |
| 833 | case MediaConstraint::DataType::Boolean: |
| 834 | m_value.asBoolean = std::exchange(other.m_value.asBoolean, nullptr); |
| 835 | break; |
| 836 | case MediaConstraint::DataType::String: |
| 837 | m_value.asString = std::exchange(other.m_value.asString, nullptr); |
| 838 | break; |
| 839 | case MediaConstraint::DataType::None: |
| 840 | ASSERT_NOT_REACHED(); |
| 841 | break; |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | MediaConstraint& constraint() const { return *m_value.asRaw; } |
| 846 | MediaConstraint::DataType dataType() const { return constraint().dataType(); } |
| 847 | MediaConstraintType constraintType() const { return constraint().constraintType(); } |
| 848 | |
| 849 | private: |
| 850 | explicit ConstraintHolder(const MediaConstraint& value) |
| 851 | { |
| 852 | switch (value.dataType()) { |
| 853 | case MediaConstraint::DataType::Integer: |
| 854 | m_value.asInteger = new IntConstraint(downcast<const IntConstraint>(value)); |
| 855 | break; |
| 856 | case MediaConstraint::DataType::Double: |
| 857 | m_value.asDouble = new DoubleConstraint(downcast<DoubleConstraint>(value)); |
| 858 | break; |
| 859 | case MediaConstraint::DataType::Boolean: |
| 860 | m_value.asBoolean = new BooleanConstraint(downcast<BooleanConstraint>(value)); |
| 861 | break; |
| 862 | case MediaConstraint::DataType::String: |
| 863 | m_value.asString = new StringConstraint(downcast<StringConstraint>(value)); |
| 864 | break; |
| 865 | case MediaConstraint::DataType::None: |
| 866 | ASSERT_NOT_REACHED(); |
| 867 | break; |
| 868 | } |
| 869 | } |
| 870 | |
| 871 | union { |
| 872 | MediaConstraint* asRaw; |
| 873 | IntConstraint* asInteger; |
| 874 | DoubleConstraint* asDouble; |
| 875 | BooleanConstraint* asBoolean; |
| 876 | StringConstraint* asString; |
| 877 | } m_value; |
| 878 | }; |
| 879 | |
| 880 | Vector<ConstraintHolder> m_variants; |
| 881 | #ifndef NDEBUG |
| 882 | int m_generation { 0 }; |
| 883 | #endif |
| 884 | }; |
| 885 | |
| 886 | struct MediaConstraints { |
| 887 | void setDefaultVideoConstraints(); |
| 888 | bool isConstraintSet(const WTF::Function<bool(const MediaTrackConstraintSetMap&)>&); |
| 889 | |
| 890 | MediaTrackConstraintSetMap mandatoryConstraints; |
| 891 | Vector<MediaTrackConstraintSetMap> advancedConstraints; |
| 892 | bool isValid { false }; |
| 893 | }; |
| 894 | |
| 895 | } // namespace WebCore |
| 896 | |
| 897 | #define SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(ConstraintType, predicate) \ |
| 898 | SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ConstraintType) \ |
| 899 | static bool isType(const WebCore::MediaConstraint& constraint) { return constraint.predicate; } \ |
| 900 | SPECIALIZE_TYPE_TRAITS_END() |
| 901 | |
| 902 | SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(IntConstraint, isInt()) |
| 903 | SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(DoubleConstraint, isDouble()) |
| 904 | SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(StringConstraint, isString()) |
| 905 | SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(BooleanConstraint, isBoolean()) |
| 906 | |
| 907 | #endif // ENABLE(MEDIA_STREAM) |
| 908 | |