c83ca74cbef3567eb71bfa47252f936066d28b90
[WebKit-https.git] / Source / WebCore / platform / mediastream / RealtimeMediaSource.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  * Copyright (C) 2013-2018 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 "Logging.h"
40 #include "MediaConstraints.h"
41 #include "NotImplemented.h"
42 #include "RealtimeMediaSourceCapabilities.h"
43 #include "RealtimeMediaSourceCenter.h"
44 #include <wtf/CompletionHandler.h>
45 #include <wtf/MainThread.h>
46 #include <wtf/UUID.h>
47 #include <wtf/text/StringHash.h>
48
49 namespace WebCore {
50
51 RealtimeMediaSource::RealtimeMediaSource(Type type, String&& name, String&& deviceID, String&& hashSalt)
52     : m_idHashSalt(WTFMove(hashSalt))
53     , m_persistentID(WTFMove(deviceID))
54     , m_type(type)
55     , m_name(WTFMove(name))
56 {
57     if (m_persistentID.isEmpty())
58         m_persistentID = createCanonicalUUIDString();
59     else
60         m_hashedID = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(m_persistentID, m_idHashSalt);
61 }
62
63 void RealtimeMediaSource::addObserver(RealtimeMediaSource::Observer& observer)
64 {
65     auto locker = holdLock(m_observersLock);
66     m_observers.add(&observer);
67 }
68
69 void RealtimeMediaSource::removeObserver(RealtimeMediaSource::Observer& observer)
70 {
71     auto locker = holdLock(m_observersLock);
72
73     m_observers.remove(&observer);
74     if (m_observers.isEmpty())
75         stop();
76 }
77
78 void RealtimeMediaSource::setInterrupted(bool interrupted, bool pageMuted)
79 {
80     if (interrupted == m_interrupted)
81         return;
82
83     m_interrupted = interrupted;
84     if (!interrupted && pageMuted)
85         return;
86
87     setMuted(interrupted);
88 }
89
90 void RealtimeMediaSource::setMuted(bool muted)
91 {
92     if (muted)
93         stop();
94     else {
95         if (interrupted())
96             return;
97
98         start();
99     }
100
101     notifyMutedChange(muted);
102 }
103
104 void RealtimeMediaSource::notifyMutedChange(bool muted)
105 {
106     if (m_muted == muted)
107         return;
108
109     m_muted = muted;
110
111     notifyMutedObservers();
112 }
113
114 void RealtimeMediaSource::setInterruptedForTesting(bool interrupted)
115 {
116     notifyMutedChange(interrupted);
117 }
118
119 void RealtimeMediaSource::forEachObserver(const WTF::Function<void(Observer&)>& apply) const
120 {
121     Vector<Observer*> observersCopy;
122     {
123         auto locker = holdLock(m_observersLock);
124         observersCopy = copyToVector(m_observers);
125     }
126     for (auto* observer : observersCopy) {
127         auto locker = holdLock(m_observersLock);
128         // Make sure the observer has not been destroyed.
129         if (!m_observers.contains(observer))
130             continue;
131         apply(*observer);
132     }
133 }
134
135 void RealtimeMediaSource::notifyMutedObservers() const
136 {
137     forEachObserver([](auto& observer) {
138         observer.sourceMutedChanged();
139     });
140 }
141
142 void RealtimeMediaSource::notifySettingsDidChangeObservers(OptionSet<RealtimeMediaSourceSettings::Flag> flags)
143 {
144     ASSERT(isMainThread());
145
146     settingsDidChange(flags);
147
148     if (m_pendingSettingsDidChangeNotification)
149         return;
150     m_pendingSettingsDidChangeNotification = true;
151
152     scheduleDeferredTask([this] {
153         m_pendingSettingsDidChangeNotification = false;
154         forEachObserver([](auto& observer) {
155             observer.sourceSettingsChanged();
156         });
157     });
158 }
159
160 void RealtimeMediaSource::videoSampleAvailable(MediaSample& mediaSample)
161 {
162     forEachObserver([&](auto& observer) {
163         observer.videoSampleAvailable(mediaSample);
164     });
165 }
166
167 void RealtimeMediaSource::audioSamplesAvailable(const MediaTime& time, const PlatformAudioData& audioData, const AudioStreamDescription& description, size_t numberOfFrames)
168 {
169     forEachObserver([&](auto& observer) {
170         observer.audioSamplesAvailable(time, audioData, description, numberOfFrames);
171     });
172 }
173
174 void RealtimeMediaSource::start()
175 {
176     if (m_isProducingData || m_isEnded)
177         return;
178
179     m_isProducingData = true;
180     startProducingData();
181
182     if (!m_isProducingData)
183         return;
184
185     forEachObserver([](auto& observer) {
186         observer.sourceStarted();
187     });
188 }
189
190 void RealtimeMediaSource::stop()
191 {
192     if (!m_isProducingData)
193         return;
194
195     m_isProducingData = false;
196     stopProducingData();
197 }
198
199 void RealtimeMediaSource::requestToEnd(Observer& callingObserver)
200 {
201     if (!m_isProducingData)
202         return;
203
204     bool hasObserverPreventingStopping = false;
205     forEachObserver([&](auto& observer) {
206         if (observer.preventSourceFromStopping())
207             hasObserverPreventingStopping = true;
208     });
209     if (hasObserverPreventingStopping)
210         return;
211
212     auto protectedThis = makeRef(*this);
213
214     stop();
215     m_isEnded = true;
216     hasEnded();
217
218     forEachObserver([callingObserver](auto& observer) {
219         if (&observer != &callingObserver)
220             observer.sourceStopped();
221     });
222 }
223
224 void RealtimeMediaSource::captureFailed()
225 {
226     RELEASE_LOG_ERROR(MediaStream, "RealtimeMediaSource::captureFailed");
227
228     m_isProducingData = false;
229     m_captureDidFailed = true;
230
231     forEachObserver([](auto& observer) {
232         observer.sourceStopped();
233     });
234 }
235
236 bool RealtimeMediaSource::supportsSizeAndFrameRate(Optional<int>, Optional<int>, Optional<double>)
237 {
238     // The size and frame rate are within the capability limits, so they are supported.
239     return true;
240 }
241
242 bool RealtimeMediaSource::supportsSizeAndFrameRate(Optional<IntConstraint> widthConstraint, Optional<IntConstraint> heightConstraint, Optional<DoubleConstraint> frameRateConstraint, String& badConstraint, double& distance)
243 {
244     if (!widthConstraint && !heightConstraint && !frameRateConstraint)
245         return true;
246
247     auto& capabilities = this->capabilities();
248
249     distance = std::numeric_limits<double>::infinity();
250
251     Optional<int> width;
252     if (widthConstraint && capabilities.supportsWidth()) {
253         double constraintDistance = fitnessDistance(*widthConstraint);
254         if (std::isinf(constraintDistance)) {
255             badConstraint = widthConstraint->name();
256             return false;
257         }
258
259         distance = std::min(distance, constraintDistance);
260         if (widthConstraint->isMandatory()) {
261             auto range = capabilities.width();
262             width = widthConstraint->valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
263         }
264     }
265
266     Optional<int> height;
267     if (heightConstraint && capabilities.supportsHeight()) {
268         double constraintDistance = fitnessDistance(*heightConstraint);
269         if (std::isinf(constraintDistance)) {
270             badConstraint = heightConstraint->name();
271             return false;
272         }
273
274         distance = std::min(distance, constraintDistance);
275         if (heightConstraint->isMandatory()) {
276             auto range = capabilities.height();
277             height = heightConstraint->valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
278         }
279     }
280
281     Optional<double> frameRate;
282     if (frameRateConstraint && capabilities.supportsFrameRate()) {
283         double constraintDistance = fitnessDistance(*frameRateConstraint);
284         if (std::isinf(constraintDistance)) {
285             badConstraint = frameRateConstraint->name();
286             return false;
287         }
288
289         distance = std::min(distance, constraintDistance);
290         if (frameRateConstraint->isMandatory()) {
291             auto range = capabilities.frameRate();
292             frameRate = frameRateConstraint->valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
293         }
294     }
295
296     // Each of the non-null values is supported individually, see if they all can be applied at the same time.
297     if (!supportsSizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate))) {
298         if (widthConstraint)
299             badConstraint = widthConstraint->name();
300         else if (heightConstraint)
301             badConstraint = heightConstraint->name();
302         else
303             badConstraint = frameRateConstraint->name();
304         return false;
305     }
306
307     return true;
308 }
309
310 double RealtimeMediaSource::fitnessDistance(const MediaConstraint& constraint)
311 {
312     auto& capabilities = this->capabilities();
313
314     switch (constraint.constraintType()) {
315     case MediaConstraintType::Width: {
316         ASSERT(constraint.isInt());
317         if (!capabilities.supportsWidth())
318             return 0;
319
320         auto range = capabilities.width();
321         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
322         break;
323     }
324
325     case MediaConstraintType::Height: {
326         ASSERT(constraint.isInt());
327         if (!capabilities.supportsHeight())
328             return 0;
329
330         auto range = capabilities.height();
331         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
332         break;
333     }
334
335     case MediaConstraintType::FrameRate: {
336         ASSERT(constraint.isDouble());
337         if (!capabilities.supportsFrameRate())
338             return 0;
339
340         auto range = capabilities.frameRate();
341         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
342         break;
343     }
344
345     case MediaConstraintType::AspectRatio: {
346         ASSERT(constraint.isDouble());
347         if (!capabilities.supportsAspectRatio())
348             return 0;
349
350         auto range = capabilities.aspectRatio();
351         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
352         break;
353     }
354
355     case MediaConstraintType::Volume: {
356         ASSERT(constraint.isDouble());
357         if (!capabilities.supportsVolume())
358             return 0;
359
360         auto range = capabilities.volume();
361         return downcast<DoubleConstraint>(constraint).fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
362         break;
363     }
364
365     case MediaConstraintType::SampleRate: {
366         ASSERT(constraint.isInt());
367         if (!capabilities.supportsSampleRate())
368             return 0;
369
370         if (auto discreteRates = discreteSampleRates())
371             return downcast<IntConstraint>(constraint).fitnessDistance(*discreteRates);
372
373         auto range = capabilities.sampleRate();
374         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
375         break;
376     }
377
378     case MediaConstraintType::SampleSize: {
379         ASSERT(constraint.isInt());
380         if (!capabilities.supportsSampleSize())
381             return 0;
382
383         if (auto discreteSizes = discreteSampleSizes())
384             return downcast<IntConstraint>(constraint).fitnessDistance(*discreteSizes);
385
386         auto range = capabilities.sampleSize();
387         return downcast<IntConstraint>(constraint).fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
388         break;
389     }
390
391     case MediaConstraintType::FacingMode: {
392         ASSERT(constraint.isString());
393         if (!capabilities.supportsFacingMode())
394             return 0;
395
396         auto& modes = capabilities.facingMode();
397         Vector<String> supportedModes;
398         supportedModes.reserveInitialCapacity(modes.size());
399         for (auto& mode : modes)
400             supportedModes.uncheckedAppend(RealtimeMediaSourceSettings::facingMode(mode));
401         return downcast<StringConstraint>(constraint).fitnessDistance(supportedModes);
402         break;
403     }
404
405     case MediaConstraintType::EchoCancellation: {
406         ASSERT(constraint.isBoolean());
407         if (!capabilities.supportsEchoCancellation())
408             return 0;
409
410         bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
411         return downcast<BooleanConstraint>(constraint).fitnessDistance(echoCancellationReadWrite);
412         break;
413     }
414
415     case MediaConstraintType::DeviceId:
416         ASSERT(!m_hashedID.isEmpty());
417         return downcast<StringConstraint>(constraint).fitnessDistance(m_hashedID);
418         break;
419
420     case MediaConstraintType::GroupId: {
421         ASSERT(constraint.isString());
422         if (!capabilities.supportsDeviceId())
423             return 0;
424
425         return downcast<StringConstraint>(constraint).fitnessDistance(settings().groupId());
426         break;
427     }
428
429     case MediaConstraintType::DisplaySurface:
430     case MediaConstraintType::LogicalSurface:
431         break;
432
433     case MediaConstraintType::Unknown:
434         // Unknown (or unsupported) constraints should be ignored.
435         break;
436     }
437
438     return 0;
439 }
440
441 template <typename ValueType>
442 static void applyNumericConstraint(const NumericConstraint<ValueType>& constraint, ValueType current, Optional<Vector<ValueType>> discreteCapabilityValues, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource& source, void (RealtimeMediaSource::*applier)(ValueType))
443 {
444     if (discreteCapabilityValues) {
445         int value = constraint.valueForDiscreteCapabilityValues(current, *discreteCapabilityValues);
446         if (value != current)
447             (source.*applier)(value);
448         return;
449     }
450
451     ValueType value = constraint.valueForCapabilityRange(current, capabilityMin, capabilityMax);
452     if (value != current)
453         (source.*applier)(value);
454 }
455
456 void RealtimeMediaSource::setSizeAndFrameRate(Optional<int> width, Optional<int> height, Optional<double> frameRate)
457 {
458     IntSize size;
459     if (width)
460         size.setWidth(width.value());
461     if (height)
462         size.setHeight(height.value());
463     setSize(size);
464     if (frameRate)
465         setFrameRate(frameRate.value());
466 }
467
468 void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
469 {
470     auto& capabilities = this->capabilities();
471     switch (constraint.constraintType()) {
472     case MediaConstraintType::Width:
473         ASSERT_NOT_REACHED();
474         break;
475
476     case MediaConstraintType::Height:
477         ASSERT_NOT_REACHED();
478         break;
479
480     case MediaConstraintType::FrameRate:
481         ASSERT_NOT_REACHED();
482         break;
483
484     case MediaConstraintType::AspectRatio: {
485         ASSERT(constraint.isDouble());
486         if (!capabilities.supportsAspectRatio())
487             return;
488
489         auto range = capabilities.aspectRatio();
490         applyNumericConstraint(downcast<DoubleConstraint>(constraint), aspectRatio(), { }, range.rangeMin().asDouble, range.rangeMax().asDouble, *this, &RealtimeMediaSource::setAspectRatio);
491         break;
492     }
493
494     case MediaConstraintType::Volume: {
495         ASSERT(constraint.isDouble());
496         if (!capabilities.supportsVolume())
497             return;
498
499         auto range = capabilities.volume();
500         applyNumericConstraint(downcast<DoubleConstraint>(constraint), volume(), { }, range.rangeMin().asDouble, range.rangeMax().asDouble, *this, &RealtimeMediaSource::setVolume);
501         break;
502     }
503
504     case MediaConstraintType::SampleRate: {
505         ASSERT(constraint.isInt());
506         if (!capabilities.supportsSampleRate())
507             return;
508
509         auto range = capabilities.sampleRate();
510         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleRate(), discreteSampleRates(), range.rangeMin().asInt, range.rangeMax().asInt, *this, &RealtimeMediaSource::setSampleRate);
511         break;
512     }
513
514     case MediaConstraintType::SampleSize: {
515         ASSERT(constraint.isInt());
516         if (!capabilities.supportsSampleSize())
517             return;
518
519         auto range = capabilities.sampleSize();
520         applyNumericConstraint(downcast<IntConstraint>(constraint), sampleSize(), { }, range.rangeMin().asInt, range.rangeMax().asInt, *this, &RealtimeMediaSource::setSampleSize);
521         break;
522     }
523
524     case MediaConstraintType::EchoCancellation: {
525         ASSERT(constraint.isBoolean());
526         if (!capabilities.supportsEchoCancellation())
527             return;
528
529         bool setting;
530         const BooleanConstraint& boolConstraint = downcast<BooleanConstraint>(constraint);
531         if (boolConstraint.getExact(setting) || boolConstraint.getIdeal(setting))
532             setEchoCancellation(setting);
533         break;
534     }
535
536     case MediaConstraintType::FacingMode: {
537         ASSERT(constraint.isString());
538         if (!capabilities.supportsFacingMode())
539             return;
540
541         auto& supportedModes = capabilities.facingMode();
542         auto filter = [supportedModes](const String& modeString) {
543             auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
544             for (auto& supportedMode : supportedModes) {
545                 if (mode == supportedMode)
546                     return true;
547             }
548             return false;
549         };
550
551         auto modeString = downcast<StringConstraint>(constraint).find(WTFMove(filter));
552         if (!modeString.isEmpty())
553             setFacingMode(RealtimeMediaSourceSettings::videoFacingModeEnum(modeString));
554         break;
555     }
556
557     case MediaConstraintType::DeviceId:
558     case MediaConstraintType::GroupId:
559         ASSERT(constraint.isString());
560         // There is nothing to do here, neither can be changed.
561         break;
562
563     case MediaConstraintType::DisplaySurface:
564     case MediaConstraintType::LogicalSurface:
565         ASSERT(constraint.isBoolean());
566         break;
567
568     case MediaConstraintType::Unknown:
569         break;
570     }
571 }
572
573 bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
574 {
575     double minimumDistance = std::numeric_limits<double>::infinity();
576
577     // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
578     //
579     // 1. Each constraint specifies one or more values (or a range of values) for its property.
580     //    A property may appear more than once in the list of 'advanced' ConstraintSets. If an
581     //    empty object or list has been given as the value for a constraint, it must be interpreted
582     //    as if the constraint were not specified (in other words, an empty constraint == no constraint).
583     //
584     //    Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
585     //    constraints will silently disappear. To avoid this being a surprise, application authors are
586     //    expected to first use the getSupportedConstraints() method as shown in the Examples below.
587
588     // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
589     //    unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
590     //    removed.)
591
592     // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
593     //    properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
594     //    distance is finite.
595
596     failedConstraint = emptyString();
597
598     // Check width, height and frame rate jointly, because while they may be supported individually the combination may not be supported.
599     double distance = std::numeric_limits<double>::infinity();
600     if (!supportsSizeAndFrameRate(constraints.mandatoryConstraints.width(), constraints.mandatoryConstraints.height(), constraints.mandatoryConstraints.frameRate(), failedConstraint, minimumDistance))
601         return false;
602
603     constraints.mandatoryConstraints.filter([&](const MediaConstraint& constraint) {
604         if (!supportsConstraint(constraint))
605             return false;
606
607         if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate) {
608             candidates.set(constraint);
609             return false;
610         }
611
612         double constraintDistance = fitnessDistance(constraint);
613         if (std::isinf(constraintDistance)) {
614             failedConstraint = constraint.name();
615             return true;
616         }
617
618         distance = std::min(distance, constraintDistance);
619         candidates.set(constraint);
620         return false;
621     });
622
623     if (!failedConstraint.isEmpty())
624         return false;
625
626     minimumDistance = distance;
627
628     // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
629     if (candidates.isEmpty())
630         return true;
631
632     // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
633     //    For each ConstraintSet:
634
635     // 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
636     //     values of properties as exact.
637     Vector<std::pair<double, MediaTrackConstraintSetMap>> supportedConstraints;
638
639     for (const auto& advancedConstraint : constraints.advancedConstraints) {
640         double constraintDistance = 0;
641         bool supported = false;
642
643         if (advancedConstraint.width() || advancedConstraint.height() || advancedConstraint.frameRate()) {
644             String dummy;
645             if (!supportsSizeAndFrameRate(advancedConstraint.width(), advancedConstraint.height(), advancedConstraint.frameRate(), dummy, constraintDistance))
646                 continue;
647
648             supported = true;
649         }
650
651         advancedConstraint.forEach([&](const MediaConstraint& constraint) {
652
653             if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate)
654                 return;
655
656             distance = fitnessDistance(constraint);
657             constraintDistance += distance;
658             if (!std::isinf(distance))
659                 supported = true;
660         });
661
662         minimumDistance = std::min(minimumDistance, constraintDistance);
663
664         // 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
665         //     settings dictionaries in candidates, discarding others.
666         //     If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
667         if (supported)
668             supportedConstraints.append({constraintDistance, advancedConstraint});
669     }
670
671     // 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
672     //    The UA should use the one with the smallest fitness distance, as calculated in step 3.
673     if (!supportedConstraints.isEmpty()) {
674         supportedConstraints.removeAllMatching([&](const std::pair<double, MediaTrackConstraintSetMap>& pair) -> bool {
675             return std::isinf(pair.first) || pair.first > minimumDistance;
676         });
677
678         if (!supportedConstraints.isEmpty()) {
679             auto& advancedConstraint = supportedConstraints[0].second;
680             advancedConstraint.forEach([&](const MediaConstraint& constraint) {
681                 candidates.merge(constraint);
682             });
683
684             minimumDistance = std::min(minimumDistance, supportedConstraints[0].first);
685         }
686     }
687
688     return true;
689 }
690
691 bool RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint)
692 {
693     auto& capabilities = this->capabilities();
694
695     switch (constraint.constraintType()) {
696     case MediaConstraintType::Width:
697         ASSERT(constraint.isInt());
698         return capabilities.supportsWidth();
699         break;
700
701     case MediaConstraintType::Height:
702         ASSERT(constraint.isInt());
703         return capabilities.supportsHeight();
704         break;
705
706     case MediaConstraintType::FrameRate:
707         ASSERT(constraint.isDouble());
708         return capabilities.supportsFrameRate();
709         break;
710
711     case MediaConstraintType::AspectRatio:
712         ASSERT(constraint.isDouble());
713         return capabilities.supportsAspectRatio();
714         break;
715
716     case MediaConstraintType::Volume:
717         ASSERT(constraint.isDouble());
718         return capabilities.supportsVolume();
719         break;
720
721     case MediaConstraintType::SampleRate:
722         ASSERT(constraint.isInt());
723         return capabilities.supportsSampleRate();
724         break;
725
726     case MediaConstraintType::SampleSize:
727         ASSERT(constraint.isInt());
728         return capabilities.supportsSampleSize();
729         break;
730
731     case MediaConstraintType::FacingMode:
732         ASSERT(constraint.isString());
733         return capabilities.supportsFacingMode();
734         break;
735
736     case MediaConstraintType::EchoCancellation:
737         ASSERT(constraint.isBoolean());
738         return capabilities.supportsEchoCancellation();
739         break;
740
741     case MediaConstraintType::DeviceId:
742         ASSERT(constraint.isString());
743         return capabilities.supportsDeviceId();
744         break;
745
746     case MediaConstraintType::GroupId:
747         ASSERT(constraint.isString());
748         return capabilities.supportsDeviceId();
749         break;
750
751     case MediaConstraintType::DisplaySurface:
752     case MediaConstraintType::LogicalSurface:
753         // https://www.w3.org/TR/screen-capture/#new-constraints-for-captured-display-surfaces
754         // 5.2.1 New Constraints for Captured Display Surfaces
755         // Since the source of media cannot be changed after a MediaStreamTrack has been returned,
756         // these constraints cannot be changed by an application.
757         return false;
758         break;
759
760     case MediaConstraintType::Unknown:
761         // Unknown (or unsupported) constraints should be ignored.
762         break;
763     }
764     
765     return false;
766 }
767
768 bool RealtimeMediaSource::supportsConstraints(const MediaConstraints& constraints, String& invalidConstraint)
769 {
770     ASSERT(constraints.isValid);
771
772     FlattenedConstraint candidates;
773     if (!selectSettings(constraints, candidates, invalidConstraint))
774         return false;
775
776     m_fitnessScore = 0;
777     for (auto& variant : candidates) {
778         double distance = fitnessDistance(variant);
779         switch (variant.constraintType()) {
780         case MediaConstraintType::DeviceId:
781         case MediaConstraintType::FacingMode:
782             m_fitnessScore += distance ? 1 : 32;
783             break;
784
785         case MediaConstraintType::Width:
786         case MediaConstraintType::Height:
787         case MediaConstraintType::FrameRate:
788         case MediaConstraintType::AspectRatio:
789         case MediaConstraintType::Volume:
790         case MediaConstraintType::SampleRate:
791         case MediaConstraintType::SampleSize:
792         case MediaConstraintType::EchoCancellation:
793         case MediaConstraintType::GroupId:
794         case MediaConstraintType::DisplaySurface:
795         case MediaConstraintType::LogicalSurface:
796         case MediaConstraintType::Unknown:
797             m_fitnessScore += distance ? 1 : 2;
798             break;
799         }
800     }
801
802     return true;
803 }
804
805 void RealtimeMediaSource::applyConstraints(const FlattenedConstraint& constraints)
806 {
807     if (constraints.isEmpty())
808         return;
809
810     beginConfiguration();
811
812     auto& capabilities = this->capabilities();
813
814     Optional<int> width;
815     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Width)) {
816         ASSERT(constraint->isInt());
817         if (capabilities.supportsWidth()) {
818             auto range = capabilities.width();
819             width = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
820         }
821     }
822
823     Optional<int> height;
824     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::Height)) {
825         ASSERT(constraint->isInt());
826         if (capabilities.supportsHeight()) {
827             auto range = capabilities.height();
828             height = downcast<IntConstraint>(*constraint).valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
829         }
830     }
831
832     Optional<double> frameRate;
833     if (const MediaConstraint* constraint = constraints.find(MediaConstraintType::FrameRate)) {
834         ASSERT(constraint->isDouble());
835         if (capabilities.supportsFrameRate()) {
836             auto range = capabilities.frameRate();
837             frameRate = downcast<DoubleConstraint>(*constraint).valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
838         }
839     }
840
841     if (width || height || frameRate)
842         setSizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate));
843
844     for (auto& variant : constraints) {
845         if (variant.constraintType() == MediaConstraintType::Width || variant.constraintType() == MediaConstraintType::Height || variant.constraintType() == MediaConstraintType::FrameRate)
846             continue;
847
848         applyConstraint(variant);
849     }
850
851     commitConfiguration();
852 }
853
854 Optional<RealtimeMediaSource::ApplyConstraintsError> RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints)
855 {
856     ASSERT(constraints.isValid);
857
858     FlattenedConstraint candidates;
859     String failedConstraint;
860     if (!selectSettings(constraints, candidates, failedConstraint))
861         return ApplyConstraintsError { failedConstraint, "Constraint not supported"_s };
862
863     applyConstraints(candidates);
864     return { };
865 }
866
867 void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, ApplyConstraintsHandler&& completionHandler)
868 {
869     completionHandler(applyConstraints(constraints));
870 }
871
872 void RealtimeMediaSource::setSize(const IntSize& size)
873 {
874     if (size == m_size)
875         return;
876
877     m_size = size;
878     notifySettingsDidChangeObservers({ RealtimeMediaSourceSettings::Flag::Width, RealtimeMediaSourceSettings::Flag::Height });
879 }
880
881 const IntSize RealtimeMediaSource::size() const
882 {
883     auto size = m_size;
884
885     if (size.isEmpty() && !m_intrinsicSize.isEmpty()) {
886         if (size.isZero())
887             size = m_intrinsicSize;
888         else if (size.width())
889             size.setHeight(size.width() * (m_intrinsicSize.height() / static_cast<double>(m_intrinsicSize.width())));
890         else if (size.height())
891             size.setWidth(size.height() * (m_intrinsicSize.width() / static_cast<double>(m_intrinsicSize.height())));
892     }
893
894     return size;
895 }
896
897 void RealtimeMediaSource::setIntrinsicSize(const IntSize& size)
898 {
899     if (m_intrinsicSize == size)
900         return;
901
902     auto currentSize = this->size();
903     m_intrinsicSize = size;
904
905     if (currentSize != this->size())
906         notifySettingsDidChangeObservers({ RealtimeMediaSourceSettings::Flag::Width, RealtimeMediaSourceSettings::Flag::Height });
907 }
908
909 const IntSize RealtimeMediaSource::intrinsicSize() const
910 {
911     return m_intrinsicSize;
912 }
913
914 void RealtimeMediaSource::setFrameRate(double rate)
915 {
916     if (m_frameRate == rate)
917         return;
918
919     m_frameRate = rate;
920     notifySettingsDidChangeObservers(RealtimeMediaSourceSettings::Flag::FrameRate);
921 }
922
923 void RealtimeMediaSource::setAspectRatio(double ratio)
924 {
925     if (m_aspectRatio == ratio)
926         return;
927
928     m_aspectRatio = ratio;
929     m_size.setHeight(m_size.width() / ratio);
930     notifySettingsDidChangeObservers({ RealtimeMediaSourceSettings::Flag::AspectRatio, RealtimeMediaSourceSettings::Flag::Height });
931 }
932
933 void RealtimeMediaSource::setFacingMode(RealtimeMediaSourceSettings::VideoFacingMode mode)
934 {
935     if (m_facingMode == mode)
936         return;
937
938     m_facingMode = mode;
939     notifySettingsDidChangeObservers(RealtimeMediaSourceSettings::Flag::FacingMode);
940 }
941
942 void RealtimeMediaSource::setVolume(double volume)
943 {
944     if (m_volume == volume)
945         return;
946
947     m_volume = volume;
948     notifySettingsDidChangeObservers(RealtimeMediaSourceSettings::Flag::Volume);
949 }
950
951 void RealtimeMediaSource::setSampleRate(int rate)
952 {
953     if (m_sampleRate == rate)
954         return;
955
956     m_sampleRate = rate;
957     notifySettingsDidChangeObservers(RealtimeMediaSourceSettings::Flag::SampleRate);
958 }
959
960 Optional<Vector<int>> RealtimeMediaSource::discreteSampleRates() const
961 {
962     return WTF::nullopt;
963 }
964
965 void RealtimeMediaSource::setSampleSize(int size)
966 {
967     if (m_sampleSize == size)
968         return;
969
970     m_sampleSize = size;
971     notifySettingsDidChangeObservers(RealtimeMediaSourceSettings::Flag::SampleSize);
972 }
973
974 Optional<Vector<int>> RealtimeMediaSource::discreteSampleSizes() const
975 {
976     return WTF::nullopt;
977 }
978
979 void RealtimeMediaSource::setEchoCancellation(bool echoCancellation)
980 {
981     if (m_echoCancellation == echoCancellation)
982         return;
983
984     m_echoCancellation = echoCancellation;
985     notifySettingsDidChangeObservers(RealtimeMediaSourceSettings::Flag::EchoCancellation);
986 }
987
988 void RealtimeMediaSource::scheduleDeferredTask(WTF::Function<void()>&& function)
989 {
990     ASSERT(function);
991     callOnMainThread([weakThis = makeWeakPtr(*this), function = WTFMove(function)] {
992         if (!weakThis)
993             return;
994
995         function();
996     });
997 }
998
999 const String& RealtimeMediaSource::hashedId() const
1000 {
1001     ASSERT(!m_hashedID.isEmpty());
1002     return m_hashedID;
1003 }
1004
1005 String RealtimeMediaSource::deviceIDHashSalt() const
1006 {
1007     return m_idHashSalt;
1008 }
1009
1010 RealtimeMediaSource::Observer::~Observer()
1011 {
1012 }
1013
1014 } // namespace WebCore
1015
1016 #endif // ENABLE(MEDIA_STREAM)