[MediaSource] Push capabilities across process boundary during UIProcess capture.
[WebKit-https.git] / Source / WebCore / platform / mediastream / RealtimeMediaSource.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
4  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
5  * Copyright (C) 2015 Ericsson AB. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer
15  *    in the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of Google Inc. nor the names of its contributors
18  *    may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "config.h"
35
36 #if ENABLE(MEDIA_STREAM)
37 #include "RealtimeMediaSource.h"
38
39 #include "MediaConstraints.h"
40 #include "NotImplemented.h"
41 #include "RealtimeMediaSourceCapabilities.h"
42 #include "UUID.h"
43 #include <wtf/MainThread.h>
44 #include <wtf/text/StringHash.h>
45
46 namespace WebCore {
47
48 RealtimeMediaSource::RealtimeMediaSource(const String& id, Type type, const String& name)
49     : m_weakPtrFactory(this)
50     , m_id(id)
51     , m_type(type)
52     , m_name(name)
53 {
54     // FIXME(147205): Need to implement fitness score for constraints
55
56     if (m_id.isEmpty())
57         m_id = createCanonicalUUIDString();
58     m_persistentID = m_id;
59     m_suppressNotifications = false;
60 }
61
62 void RealtimeMediaSource::reset()
63 {
64     m_stopped = false;
65     m_muted = false;
66 }
67
68 void RealtimeMediaSource::addObserver(RealtimeMediaSource::Observer& observer)
69 {
70     m_observers.append(&observer);
71 }
72
73 void RealtimeMediaSource::removeObserver(RealtimeMediaSource::Observer& observer)
74 {
75     m_observers.removeFirstMatching([&observer](auto* anObserver) {
76         return anObserver == &observer;
77     });
78
79     if (!m_observers.size())
80         stop();
81 }
82
83 void RealtimeMediaSource::setMuted(bool muted)
84 {
85     if (m_stopped || m_muted == muted)
86         return;
87
88     m_muted = muted;
89
90     if (muted) {
91         // FIXME: We need to figure out how to guarantee that at least one black video frame is
92         // emitted after being muted.
93         stopProducingData();
94     } else
95         startProducingData();
96
97     notifyMutedObservers();
98 }
99
100 void RealtimeMediaSource::notifyMutedObservers() const
101 {
102     for (auto& observer : m_observers)
103         observer->sourceMutedChanged();
104 }
105
106 void RealtimeMediaSource::setEnabled(bool enabled)
107 {
108     if (m_stopped || m_enabled == enabled)
109         return;
110
111     m_enabled = enabled;
112
113     if (m_stopped)
114         return;
115
116     for (auto& observer : m_observers)
117         observer->sourceEnabledChanged();
118 }
119
120 void RealtimeMediaSource::settingsDidChange()
121 {
122     ASSERT(isMainThread());
123
124     if (m_pendingSettingsDidChangeNotification || m_suppressNotifications)
125         return;
126
127     m_pendingSettingsDidChangeNotification = true;
128
129     scheduleDeferredTask([this] {
130         m_pendingSettingsDidChangeNotification = false;
131         for (auto& observer : m_observers)
132             observer->sourceSettingsChanged();
133     });
134 }
135
136 void RealtimeMediaSource::videoSampleAvailable(MediaSample& mediaSample)
137 {
138     ASSERT(isMainThread());
139     for (const auto& observer : m_observers)
140         observer->videoSampleAvailable(mediaSample);
141 }
142
143 void RealtimeMediaSource::audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames)
144 {
145     for (const auto& observer : m_observers)
146         observer->audioSamplesAvailable(time, audioData, description, numberOfFrames);
147 }
148
149 void RealtimeMediaSource::stop(Observer* callingObserver)
150 {
151     if (stopped())
152         return;
153
154     m_stopped = true;
155
156     for (const auto& observer : m_observers) {
157         if (observer != callingObserver)
158             observer->sourceStopped();
159     }
160
161     stopProducingData();
162 }
163
164 void RealtimeMediaSource::requestStop(Observer* callingObserver)
165 {
166     if (stopped())
167         return;
168
169     for (const auto& observer : m_observers) {
170         if (observer->preventSourceFromStopping())
171             return;
172     }
173     stop(callingObserver);
174 }
175
176 bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<int>, std::optional<int>, std::optional<double>)
177 {
178     // The size and frame rate are within the capability limits, so they are supported.
179     return true;
180 }
181
182 bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<IntConstraint> widthConstraint, std::optional<IntConstraint> heightConstraint, std::optional<DoubleConstraint> frameRateConstraint, String& badConstraint, double& distance)
183 {
184     if (!widthConstraint && !heightConstraint && !frameRateConstraint)
185         return true;
186
187     auto& capabilities = this->capabilities();
188
189     distance = std::numeric_limits<double>::infinity();
190
191     std::optional<int> width;
192     if (widthConstraint && capabilities.supportsWidth()) {
193         double constraintDistance = fitnessDistance(*widthConstraint);
194         if (std::isinf(constraintDistance)) {
195             badConstraint = widthConstraint->name();
196             return false;
197         }
198
199         distance = std::min(distance, constraintDistance);
200         auto range = capabilities.width();
201         width = widthConstraint->valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
202     }
203
204     std::optional<int> height;
205     if (heightConstraint && capabilities.supportsHeight()) {
206         double constraintDistance = fitnessDistance(*heightConstraint);
207         if (std::isinf(constraintDistance)) {
208             badConstraint = heightConstraint->name();
209             return false;
210         }
211
212         distance = std::min(distance, constraintDistance);
213         auto range = capabilities.height();
214         height = heightConstraint->valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
215     }
216
217     std::optional<double> frameRate;
218     if (frameRateConstraint && capabilities.supportsFrameRate()) {
219         double constraintDistance = fitnessDistance(*frameRateConstraint);
220         if (std::isinf(constraintDistance)) {
221             badConstraint = frameRateConstraint->name();
222             return false;
223         }
224
225         distance = std::min(distance, constraintDistance);
226         auto range = capabilities.frameRate();
227         frameRate = frameRateConstraint->valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
228     }
229
230     // Each of the non-null values is supported individually, see if they all can be applied at the same time.
231     if (!supportsSizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate))) {
232         if (widthConstraint)
233             badConstraint = widthConstraint->name();
234         else if (heightConstraint)
235             badConstraint = heightConstraint->name();
236         else
237             badConstraint = frameRateConstraint->name();
238         return false;
239     }
240     
241     return true;
242 }
243
244 double RealtimeMediaSource::fitnessDistance(const MediaConstraint& constraint)
245 {
246     auto& capabilities = this->capabilities();
247
248     switch (constraint.constraintType()) {
249     case MediaConstraintType::Width: {
250         ASSERT(constraint.isInt());
251         if (!capabilities.supportsWidth())
252             return 0;
253
254         auto range = capabilities.width();
255         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
256         break;
257     }
258
259     case MediaConstraintType::Height: {
260         ASSERT(constraint.isInt());
261         if (!capabilities.supportsHeight())
262             return 0;
263
264         auto range = capabilities.height();
265         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
266         break;
267     }
268
269     case MediaConstraintType::FrameRate: {
270         ASSERT(constraint.isDouble());
271         if (!capabilities.supportsFrameRate())
272             return 0;
273
274         auto range = capabilities.frameRate();
275         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
276         break;
277     }
278
279     case MediaConstraintType::AspectRatio: {
280         ASSERT(constraint.isDouble());
281         if (!capabilities.supportsAspectRatio())
282             return 0;
283
284         auto range = capabilities.aspectRatio();
285         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
286         break;
287     }
288
289     case MediaConstraintType::Volume: {
290         ASSERT(constraint.isDouble());
291         if (!capabilities.supportsVolume())
292             return 0;
293
294         auto range = capabilities.volume();
295         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
296         break;
297     }
298
299     case MediaConstraintType::SampleRate: {
300         ASSERT(constraint.isInt());
301         if (!capabilities.supportsSampleRate())
302             return 0;
303
304         auto range = capabilities.sampleRate();
305         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
306         break;
307     }
308
309     case MediaConstraintType::SampleSize: {
310         ASSERT(constraint.isInt());
311         if (!capabilities.supportsSampleSize())
312             return 0;
313
314         auto range = capabilities.sampleSize();
315         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
316         break;
317     }
318
319     case MediaConstraintType::FacingMode: {
320         ASSERT(constraint.isString());
321         if (!capabilities.supportsFacingMode())
322             return 0;
323
324         auto& modes = capabilities.facingMode();
325         Vector<String> supportedModes;
326         supportedModes.reserveInitialCapacity(modes.size());
327         for (auto& mode : modes)
328             supportedModes.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
329         return downcast<StringConstraint>(constraint).fitnessDistance(supportedModes);
330         break;
331     }
332
333     case MediaConstraintType::EchoCancellation: {
334         ASSERT(constraint.isBoolean());
335         if (!capabilities.supportsEchoCancellation())
336             return 0;
337
338         bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
339         return downcast<BooleanConstraint>(constraint).fitnessDistance(echoCancellationReadWrite);
340         break;
341     }
342
343     case MediaConstraintType::DeviceId: {
344         ASSERT(constraint.isString());
345         if (!capabilities.supportsDeviceId())
346             return 0;
347
348         return downcast<StringConstraint>(constraint).fitnessDistance(m_id);
349         break;
350     }
351
352     case MediaConstraintType::GroupId: {
353         ASSERT(constraint.isString());
354         if (!capabilities.supportsDeviceId())
355             return 0;
356
357         return downcast<StringConstraint>(constraint).fitnessDistance(settings().groupId());
358         break;
359     }
360
361     case MediaConstraintType::Unknown:
362         // Unknown (or unsupported) constraints should be ignored.
363         break;
364     }
365
366     return 0;
367 }
368
369 template <typename ValueType>
370 static void applyNumericConstraint(const NumericConstraint<ValueType>& constraint, ValueType current, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource* source, void (RealtimeMediaSource::*applier)(ValueType))
371 {
372     ValueType value = constraint.valueForCapabilityRange(current, capabilityMin, capabilityMax);
373     if (value != current)
374         (source->*applier)(value);
375 }
376
377 void RealtimeMediaSource::applySizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double> frameRate)
378 {
379     if (width)
380         setWidth(width.value());
381     if (height)
382         setHeight(height.value());
383     if (frameRate)
384         setFrameRate(frameRate.value());
385 }
386
387 void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
388 {
389     auto& capabilities = this->capabilities();
390     switch (constraint.constraintType()) {
391     case MediaConstraintType::Width: {
392         ASSERT(constraint.isInt());
393         if (!capabilities.supportsWidth())
394             return;
395
396         auto range = capabilities.width();
397         applyNumericConstraint(downcast<IntConstraint>(constraint), size().width(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setWidth);
398         break;
399     }
400
401     case MediaConstraintType::Height: {
402         ASSERT(constraint.isInt());
403         if (!capabilities.supportsHeight())
404             return;
405
406         auto range = capabilities.height();
407         applyNumericConstraint(downcast<IntConstraint>(constraint), size().height(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setHeight);
408         break;
409     }
410
411     case MediaConstraintType::FrameRate: {
412         ASSERT(constraint.isDouble());
413         if (!capabilities.supportsFrameRate())
414             return;
415
416         auto range = capabilities.frameRate();
417         applyNumericConstraint(downcast<DoubleConstraint>(constraint), frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setFrameRate);
418         break;
419     }
420
421     case MediaConstraintType::AspectRatio: {
422         ASSERT(constraint.isDouble());
423         if (!capabilities.supportsAspectRatio())
424             return;
425
426         auto range = capabilities.aspectRatio();
427         applyNumericConstraint(downcast<DoubleConstraint>(constraint), aspectRatio(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setAspectRatio);
428         break;
429     }
430
431     case MediaConstraintType::Volume: {
432         ASSERT(constraint.isDouble());
433         if (!capabilities.supportsVolume())
434             return;
435
436         auto range = capabilities.volume();
437         applyNumericConstraint(downcast<DoubleConstraint>(constraint), volume(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setVolume);
438         break;
439     }
440
441     case MediaConstraintType::SampleRate: {
442         ASSERT(constraint.isInt());
443         if (!capabilities.supportsSampleRate())
444             return;
445
446         auto range = capabilities.sampleRate();
447         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleRate(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setSampleRate);
448         break;
449     }
450
451     case MediaConstraintType::SampleSize: {
452         ASSERT(constraint.isInt());
453         if (!capabilities.supportsSampleSize())
454             return;
455
456         auto range = capabilities.sampleSize();
457         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleSize(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setSampleSize);
458         break;
459     }
460
461     case MediaConstraintType::EchoCancellation: {
462         ASSERT(constraint.isBoolean());
463         if (!capabilities.supportsEchoCancellation())
464             return;
465
466         bool setting;
467         const BooleanConstraint& boolConstraint = downcast<BooleanConstraint>(constraint);
468         if (boolConstraint.getExact(setting) || boolConstraint.getIdeal(setting))
469             setEchoCancellation(setting);
470         break;
471     }
472
473     case MediaConstraintType::FacingMode: {
474         ASSERT(constraint.isString());
475         if (!capabilities.supportsFacingMode())
476             return;
477
478         auto& supportedModes = capabilities.facingMode();
479         auto filter = [supportedModes](const String& modeString) {
480             auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
481             for (auto& supportedMode : supportedModes) {
482                 if (mode == supportedMode)
483                     return true;
484             }
485             return false;
486         };
487
488         auto modeString = downcast<StringConstraint>(constraint).find(filter);
489         if (!modeString.isEmpty())
490             setFacingMode(RealtimeMediaSourceSettings::videoFacingModeEnum(modeString));
491         break;
492     }
493
494     case MediaConstraintType::DeviceId:
495     case MediaConstraintType::GroupId:
496         ASSERT(constraint.isString());
497         // There is nothing to do here, neither can be changed.
498         break;
499
500     case MediaConstraintType::Unknown:
501         break;
502     }
503 }
504
505 bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
506 {
507     m_fitnessScore = std::numeric_limits<double>::infinity();
508
509     // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
510     //
511     // 1. Each constraint specifies one or more values (or a range of values) for its property.
512     //    A property may appear more than once in the list of 'advanced' ConstraintSets. If an
513     //    empty object or list has been given as the value for a constraint, it must be interpreted
514     //    as if the constraint were not specified (in other words, an empty constraint == no constraint).
515     //
516     //    Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
517     //    constraints will silently disappear. To avoid this being a surprise, application authors are
518     //    expected to first use the getSupportedConstraints() method as shown in the Examples below.
519
520     // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
521     //    unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
522     //    removed.)
523
524     // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
525     //    properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
526     //    distance is finite.
527
528     failedConstraint = emptyString();
529
530     // Check width, height, and frame rate separately, because while they may be supported individually the combination may not be supported.
531     double distance = std::numeric_limits<double>::infinity();
532     if (!supportsSizeAndFrameRate(constraints.mandatoryConstraints().width(), constraints.mandatoryConstraints().height(), constraints.mandatoryConstraints().frameRate(), failedConstraint, m_fitnessScore))
533         return false;
534
535     constraints.mandatoryConstraints().filter([&](const MediaConstraint& constraint) {
536         if (!supportsConstraint(constraint))
537             return false;
538
539         if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate) {
540             candidates.set(constraint);
541             return false;
542         }
543
544         double constraintDistance = fitnessDistance(constraint);
545         if (std::isinf(constraintDistance)) {
546             failedConstraint = constraint.name();
547             return true;
548         }
549
550         distance = std::min(distance, constraintDistance);
551         candidates.set(constraint);
552         return false;
553     });
554
555     if (!failedConstraint.isEmpty())
556         return false;
557
558     m_fitnessScore = distance;
559
560     // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
561     if (candidates.isEmpty())
562         return true;
563
564     // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
565     //    For each ConstraintSet:
566
567     // 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
568     //     values of properties as exact.
569     Vector<std::pair<double, MediaTrackConstraintSetMap>> supportedConstraints;
570
571     for (const auto& advancedConstraint : constraints.advancedConstraints()) {
572         double constraintDistance = 0;
573         bool supported = false;
574
575         advancedConstraint.forEach([&](const MediaConstraint& constraint) {
576             distance = fitnessDistance(constraint);
577             constraintDistance += distance;
578             if (!std::isinf(distance))
579                 supported = true;
580         });
581
582         m_fitnessScore = std::min(m_fitnessScore, constraintDistance);
583
584         // 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
585         //     settings dictionaries in candidates, discarding others.
586         //     If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
587         if (supported)
588             supportedConstraints.append({constraintDistance, advancedConstraint});
589     }
590
591     // 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
592     //    The UA should use the one with the smallest fitness distance, as calculated in step 3.
593     if (!supportedConstraints.isEmpty()) {
594         supportedConstraints.removeAllMatching([&](const std::pair<double, MediaTrackConstraintSetMap>& pair) -> bool {
595             return std::isinf(pair.first) || pair.first > m_fitnessScore;
596         });
597
598         if (!supportedConstraints.isEmpty()) {
599             auto& advancedConstraint = supportedConstraints[0].second;
600             advancedConstraint.forEach([&](const MediaConstraint& constraint) {
601                 candidates.merge(constraint);
602             });
603
604             m_fitnessScore = std::min(m_fitnessScore, supportedConstraints[0].first);
605         }
606     }
607
608     return true;
609 }
610
611 bool RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint) const
612 {
613     auto& capabilities = this->capabilities();
614
615     switch (constraint.constraintType()) {
616     case MediaConstraintType::Width:
617         ASSERT(constraint.isInt());
618         return capabilities.supportsWidth();
619         break;
620
621     case MediaConstraintType::Height:
622         ASSERT(constraint.isInt());
623         return capabilities.supportsHeight();
624         break;
625
626     case MediaConstraintType::FrameRate:
627         ASSERT(constraint.isDouble());
628         return capabilities.supportsFrameRate();
629         break;
630
631     case MediaConstraintType::AspectRatio:
632         ASSERT(constraint.isDouble());
633         return capabilities.supportsAspectRatio();
634         break;
635
636     case MediaConstraintType::Volume:
637         ASSERT(constraint.isDouble());
638         return capabilities.supportsVolume();
639         break;
640
641     case MediaConstraintType::SampleRate:
642         ASSERT(constraint.isInt());
643         return capabilities.supportsSampleRate();
644         break;
645
646     case MediaConstraintType::SampleSize:
647         ASSERT(constraint.isInt());
648         return capabilities.supportsSampleSize();
649         break;
650
651     case MediaConstraintType::FacingMode:
652         ASSERT(constraint.isString());
653         return capabilities.supportsFacingMode();
654         break;
655
656     case MediaConstraintType::EchoCancellation:
657         ASSERT(constraint.isBoolean());
658         return capabilities.supportsEchoCancellation();
659         break;
660
661     case MediaConstraintType::DeviceId:
662         ASSERT(constraint.isString());
663         return capabilities.supportsDeviceId();
664         break;
665
666     case MediaConstraintType::GroupId:
667         ASSERT(constraint.isString());
668         return capabilities.supportsDeviceId();
669         break;
670
671     case MediaConstraintType::Unknown:
672         // Unknown (or unsupported) constraints should be ignored.
673         break;
674     }
675     
676     return false;
677 }
678
679 bool RealtimeMediaSource::supportsConstraints(const MediaConstraints& constraints, String& invalidConstraint)
680 {
681     ASSERT(constraints.isValid());
682
683     FlattenedConstraint candidates;
684     if (!selectSettings(constraints, candidates, invalidConstraint))
685         return false;
686     
687     return true;
688 }
689
690 void RealtimeMediaSource::applyConstraints(const FlattenedConstraint& constraints)
691 {
692     if (constraints.isEmpty())
693         return;
694
695     beginConfiguration();
696
697     auto& capabilities = this->capabilities();
698
699     std::optional<int> width;
700     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Width)) {
701         ASSERT(constraint->isInt());
702         if (capabilities.supportsWidth()) {
703             auto range = capabilities.width();
704             width = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
705         }
706     }
707
708     std::optional<int> height;
709     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Height)) {
710         ASSERT(constraint->isInt());
711         if (capabilities.supportsHeight()) {
712             auto range = capabilities.height();
713             height = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
714         }
715     }
716
717     std::optional<double> frameRate;
718     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::FrameRate)) {
719         ASSERT(constraint->isDouble());
720         if (capabilities.supportsFrameRate()) {
721             auto range = capabilities.frameRate();
722             frameRate = downcast<DoubleConstraint>(*constraint).valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
723         }
724     }
725
726     // FIXME: applySizeAndFrameRate should take MediaConstraint* instead of std::optional<> so it can see if a constraint is an exact, min, max,
727     // or ideal, and choose the correct value for properties with non-discreet capabilities when necessary.
728     if (width || height || frameRate)
729         applySizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate));
730
731     for (auto& variant : constraints) {
732         if (variant.constraintType() == MediaConstraintType::Width || variant.constraintType() == MediaConstraintType::Height || variant.constraintType() == MediaConstraintType::FrameRate)
733             continue;
734
735         applyConstraint(variant);
736     }
737
738     commitConfiguration();
739 }
740
741 std::optional<std::pair<String, String>> RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints)
742 {
743     ASSERT(constraints.isValid());
744
745     FlattenedConstraint candidates;
746     String failedConstraint;
747     if (!selectSettings(constraints, candidates, failedConstraint))
748         return { { failedConstraint, ASCIILiteral("Constraint not supported") } };
749
750     applyConstraints(candidates);
751     return std::nullopt;
752 }
753
754 void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
755 {
756     auto result = applyConstraints(constraints);
757     if (!result && successHandler)
758         successHandler();
759     else if (result && failureHandler)
760         failureHandler(result.value().first, result.value().second);
761 }
762
763 void RealtimeMediaSource::setWidth(int width)
764 {
765     if (width == m_size.width())
766         return;
767
768     int height = m_aspectRatio ? width / m_aspectRatio : m_size.height();
769     if (!applySize(IntSize(width, height)))
770         return;
771
772     m_size.setWidth(width);
773     if (m_aspectRatio)
774         m_size.setHeight(width / m_aspectRatio);
775
776     settingsDidChange();
777 }
778
779 void RealtimeMediaSource::setHeight(int height)
780 {
781     if (height == m_size.height())
782         return;
783
784     int width = m_aspectRatio ? height * m_aspectRatio : m_size.width();
785     if (!applySize(IntSize(width, height)))
786         return;
787
788     if (m_aspectRatio)
789         m_size.setWidth(width);
790     m_size.setHeight(height);
791
792     settingsDidChange();
793 }
794
795 void RealtimeMediaSource::setFrameRate(double rate)
796 {
797     if (m_frameRate == rate || !applyFrameRate(rate))
798         return;
799
800     m_frameRate = rate;
801     settingsDidChange();
802 }
803
804 void RealtimeMediaSource::setAspectRatio(double ratio)
805 {
806     if (m_aspectRatio == ratio || !applyAspectRatio(ratio))
807         return;
808
809     m_aspectRatio = ratio;
810     m_size.setHeight(m_size.width() / ratio);
811     settingsDidChange();
812 }
813
814 void RealtimeMediaSource::setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
815 {
816     if (m_facingMode == mode || !applyFacingMode(mode))
817         return;
818
819     m_facingMode = mode;
820     settingsDidChange();
821 }
822
823 void RealtimeMediaSource::setVolume(double volume)
824 {
825     if (m_volume == volume || !applyVolume(volume))
826         return;
827
828     m_volume = volume;
829     settingsDidChange();
830 }
831
832 void RealtimeMediaSource::setSampleRate(int rate)
833 {
834     if (m_sampleRate == rate || !applySampleRate(rate))
835         return;
836
837     m_sampleRate = rate;
838     settingsDidChange();
839 }
840
841 void RealtimeMediaSource::setSampleSize(int size)
842 {
843     if (m_sampleSize == size || !applySampleSize(size))
844         return;
845
846     m_sampleSize = size;
847     settingsDidChange();
848 }
849
850 void RealtimeMediaSource::setEchoCancellation(bool echoCancellation)
851 {
852     if (m_echoCancellation == echoCancellation || !applyEchoCancellation(echoCancellation))
853         return;
854
855     m_echoCancellation = echoCancellation;
856     settingsDidChange();
857 }
858
859 void RealtimeMediaSource::scheduleDeferredTask(std::function<void()>&& function)
860 {
861     ASSERT(function);
862     callOnMainThread([weakThis = createWeakPtr(), function = WTFMove(function)] {
863         if (!weakThis)
864             return;
865
866         function();
867     });
868 }
869
870 } // namespace WebCore
871
872 #endif // ENABLE(MEDIA_STREAM)