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 | |