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
41namespace WebCore {
42
43class MediaConstraint {
44public:
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
77protected:
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
88private:
89 String m_name;
90 MediaConstraintType m_constraintType { MediaConstraintType::Unknown };
91 DataType m_dataType { DataType::None };
92};
93
94template<class ValueType>
95class NumericConstraint : public MediaConstraint {
96public:
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
363protected:
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
403class IntConstraint final : public NumericConstraint<int> {
404public:
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
419class DoubleConstraint final : public NumericConstraint<double> {
420public:
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
435class BooleanConstraint final : public MediaConstraint {
436public:
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
529private:
530 Optional<bool> m_exact;
531 Optional<bool> m_ideal;
532};
533
534class StringConstraint : public MediaConstraint {
535public:
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
623private:
624 Vector<String> m_exact;
625 Vector<String> m_ideal;
626};
627
628class UnknownConstraint final : public MediaConstraint {
629public:
630 UnknownConstraint(const String& name, MediaConstraintType type)
631 : MediaConstraint(name, type, DataType::None)
632 {
633 }
634
635private:
636 bool isEmpty() const { return true; }
637 bool isMandatory() const { return false; }
638 void merge(const MediaConstraint&) { }
639};
640
641class MediaTrackConstraintSetMap {
642public:
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
726private:
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
745class FlattenedConstraint {
746public:
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
793private:
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
886struct 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) \
898SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ConstraintType) \
899static bool isType(const WebCore::MediaConstraint& constraint) { return constraint.predicate; } \
900SPECIALIZE_TYPE_TRAITS_END()
901
902SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(IntConstraint, isInt())
903SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(DoubleConstraint, isDouble())
904SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(StringConstraint, isString())
905SPECIALIZE_TYPE_TRAITS_MEDIACONSTRAINT(BooleanConstraint, isBoolean())
906
907#endif // ENABLE(MEDIA_STREAM)
908