Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : // This file should not be included by other includes, as it contains code
6 :
7 : #ifndef MEDIATRACKCONSTRAINTS_H_
8 : #define MEDIATRACKCONSTRAINTS_H_
9 :
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/dom/MediaStreamTrackBinding.h"
12 : #include "mozilla/dom/MediaTrackConstraintSetBinding.h"
13 : #include "mozilla/dom/MediaTrackSupportedConstraintsBinding.h"
14 :
15 : #include <map>
16 : #include <set>
17 : #include <vector>
18 :
19 : namespace mozilla {
20 :
21 : template<class EnumValuesStrings, class Enum>
22 0 : static const char* EnumToASCII(const EnumValuesStrings& aStrings, Enum aValue) {
23 0 : return aStrings[uint32_t(aValue)].value;
24 : }
25 :
26 : template<class EnumValuesStrings, class Enum>
27 0 : static Enum StringToEnum(const EnumValuesStrings& aStrings,
28 : const nsAString& aValue, Enum aDefaultValue) {
29 0 : for (size_t i = 0; aStrings[i].value; i++) {
30 0 : if (aValue.EqualsASCII(aStrings[i].value)) {
31 0 : return Enum(i);
32 : }
33 : }
34 0 : return aDefaultValue;
35 : }
36 :
37 : // Helper classes for orthogonal constraints without interdependencies.
38 : // Instead of constraining values, constrain the constraints themselves.
39 :
40 0 : class NormalizedConstraintSet
41 : {
42 : protected:
43 0 : class BaseRange
44 : {
45 : protected:
46 : typedef BaseRange NormalizedConstraintSet::* MemberPtrType;
47 :
48 0 : BaseRange(MemberPtrType aMemberPtr, const char* aName,
49 0 : nsTArray<MemberPtrType>* aList) : mName(aName) {
50 0 : if (aList) {
51 0 : aList->AppendElement(aMemberPtr);
52 : }
53 0 : }
54 0 : virtual ~BaseRange() {}
55 : public:
56 : virtual bool Merge(const BaseRange& aOther) = 0;
57 : virtual void FinalizeMerge() = 0;
58 :
59 : const char* mName;
60 : };
61 :
62 : typedef BaseRange NormalizedConstraintSet::* MemberPtrType;
63 :
64 : public:
65 : template<class ValueType>
66 0 : class Range : public BaseRange
67 : {
68 : public:
69 : ValueType mMin, mMax;
70 : Maybe<ValueType> mIdeal;
71 :
72 0 : Range(MemberPtrType aMemberPtr, const char* aName, ValueType aMin,
73 : ValueType aMax, nsTArray<MemberPtrType>* aList)
74 : : BaseRange(aMemberPtr, aName, aList)
75 0 : , mMin(aMin), mMax(aMax), mMergeDenominator(0) {}
76 0 : virtual ~Range() {};
77 :
78 : template<class ConstrainRange>
79 : void SetFrom(const ConstrainRange& aOther);
80 0 : ValueType Clamp(ValueType n) const { return std::max(mMin, std::min(n, mMax)); }
81 0 : ValueType Get(ValueType defaultValue) const {
82 0 : return Clamp(mIdeal.valueOr(defaultValue));
83 : }
84 0 : bool Intersects(const Range& aOther) const {
85 0 : return mMax >= aOther.mMin && mMin <= aOther.mMax;
86 : }
87 0 : void Intersect(const Range& aOther) {
88 0 : MOZ_ASSERT(Intersects(aOther));
89 0 : mMin = std::max(mMin, aOther.mMin);
90 0 : mMax = std::min(mMax, aOther.mMax);
91 0 : }
92 0 : bool Merge(const Range& aOther) {
93 0 : if (!Intersects(aOther)) {
94 0 : return false;
95 : }
96 0 : Intersect(aOther);
97 :
98 0 : if (aOther.mIdeal.isSome()) {
99 : // Ideal values, as stored, may be outside their min max range, so use
100 : // clamped values in averaging, to avoid extreme outliers skewing results.
101 0 : if (mIdeal.isNothing()) {
102 0 : mIdeal.emplace(aOther.Get(0));
103 0 : mMergeDenominator = 1;
104 : } else {
105 0 : if (!mMergeDenominator) {
106 0 : *mIdeal = Get(0);
107 0 : mMergeDenominator = 1;
108 : }
109 0 : *mIdeal += aOther.Get(0);
110 0 : mMergeDenominator++;
111 : }
112 : }
113 0 : return true;
114 : }
115 0 : void FinalizeMerge() override
116 : {
117 0 : if (mMergeDenominator) {
118 0 : *mIdeal /= mMergeDenominator;
119 0 : mMergeDenominator = 0;
120 : }
121 0 : }
122 0 : void TakeHighestIdeal(const Range& aOther) {
123 0 : if (aOther.mIdeal.isSome()) {
124 0 : if (mIdeal.isNothing()) {
125 0 : mIdeal.emplace(aOther.Get(0));
126 : } else {
127 0 : *mIdeal = std::max(Get(0), aOther.Get(0));
128 : }
129 : }
130 0 : }
131 : private:
132 0 : bool Merge(const BaseRange& aOther) override {
133 0 : return Merge(static_cast<const Range&>(aOther));
134 : }
135 :
136 : uint32_t mMergeDenominator;
137 : };
138 :
139 0 : struct LongRange : public Range<int32_t>
140 : {
141 : typedef LongRange NormalizedConstraintSet::* LongPtrType;
142 :
143 : LongRange(LongPtrType aMemberPtr, const char* aName,
144 : const dom::OwningLongOrConstrainLongRange& aOther, bool advanced,
145 : nsTArray<MemberPtrType>* aList);
146 : };
147 :
148 0 : struct LongLongRange : public Range<int64_t>
149 : {
150 : typedef LongLongRange NormalizedConstraintSet::* LongLongPtrType;
151 :
152 : LongLongRange(LongLongPtrType aMemberPtr, const char* aName,
153 : const long long& aOther,
154 : nsTArray<MemberPtrType>* aList);
155 : };
156 :
157 0 : struct DoubleRange : public Range<double>
158 : {
159 : typedef DoubleRange NormalizedConstraintSet::* DoublePtrType;
160 :
161 : DoubleRange(DoublePtrType aMemberPtr,
162 : const char* aName,
163 : const dom::OwningDoubleOrConstrainDoubleRange& aOther,
164 : bool advanced,
165 : nsTArray<MemberPtrType>* aList);
166 : };
167 :
168 0 : struct BooleanRange : public Range<bool>
169 : {
170 : typedef BooleanRange NormalizedConstraintSet::* BooleanPtrType;
171 :
172 : BooleanRange(BooleanPtrType aMemberPtr, const char* aName,
173 : const dom::OwningBooleanOrConstrainBooleanParameters& aOther,
174 : bool advanced,
175 : nsTArray<MemberPtrType>* aList);
176 :
177 0 : BooleanRange(BooleanPtrType aMemberPtr, const char* aName, const bool& aOther,
178 : nsTArray<MemberPtrType>* aList)
179 0 : : Range<bool>((MemberPtrType)aMemberPtr, aName, false, true, aList) {
180 0 : mIdeal.emplace(aOther);
181 0 : }
182 : };
183 :
184 0 : struct StringRange : public BaseRange
185 : {
186 : typedef std::set<nsString> ValueType;
187 : ValueType mExact, mIdeal;
188 :
189 : typedef StringRange NormalizedConstraintSet::* StringPtrType;
190 :
191 : StringRange(StringPtrType aMemberPtr, const char* aName,
192 : const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aOther,
193 : bool advanced,
194 : nsTArray<MemberPtrType>* aList);
195 :
196 0 : StringRange(StringPtrType aMemberPtr, const char* aName,
197 : const nsString& aOther, nsTArray<MemberPtrType>* aList)
198 0 : : BaseRange((MemberPtrType)aMemberPtr, aName, aList) {
199 0 : mIdeal.insert(aOther);
200 0 : }
201 :
202 0 : ~StringRange() {}
203 :
204 : void SetFrom(const dom::ConstrainDOMStringParameters& aOther);
205 : ValueType Clamp(const ValueType& n) const;
206 : ValueType Get(const ValueType& defaultValue) const {
207 : return Clamp(mIdeal.size() ? mIdeal : defaultValue);
208 : }
209 : bool Intersects(const StringRange& aOther) const;
210 : void Intersect(const StringRange& aOther);
211 : bool Merge(const StringRange& aOther);
212 0 : void FinalizeMerge() override {}
213 : private:
214 0 : bool Merge(const BaseRange& aOther) override {
215 0 : return Merge(static_cast<const StringRange&>(aOther));
216 : }
217 : };
218 :
219 : // All new constraints should be added here whether they use flattening or not
220 : LongRange mWidth, mHeight;
221 : DoubleRange mFrameRate;
222 : StringRange mFacingMode;
223 : StringRange mMediaSource;
224 : LongLongRange mBrowserWindow;
225 : BooleanRange mScrollWithPage;
226 : StringRange mDeviceId;
227 : LongRange mViewportOffsetX, mViewportOffsetY, mViewportWidth, mViewportHeight;
228 : BooleanRange mEchoCancellation, mNoiseSuppression, mAutoGainControl;
229 : LongRange mChannelCount;
230 : private:
231 : typedef NormalizedConstraintSet T;
232 : public:
233 0 : NormalizedConstraintSet(const dom::MediaTrackConstraintSet& aOther,
234 : bool advanced,
235 : nsTArray<MemberPtrType>* aList = nullptr)
236 0 : : mWidth(&T::mWidth, "width", aOther.mWidth, advanced, aList)
237 : , mHeight(&T::mHeight, "height", aOther.mHeight, advanced, aList)
238 : , mFrameRate(&T::mFrameRate, "frameRate", aOther.mFrameRate, advanced, aList)
239 : , mFacingMode(&T::mFacingMode, "facingMode", aOther.mFacingMode, advanced, aList)
240 : , mMediaSource(&T::mMediaSource, "mediaSource", aOther.mMediaSource, aList)
241 : , mBrowserWindow(&T::mBrowserWindow, "browserWindow",
242 0 : aOther.mBrowserWindow.WasPassed() ?
243 0 : aOther.mBrowserWindow.Value() : 0, aList)
244 : , mScrollWithPage(&T::mScrollWithPage, "scrollWithPage",
245 0 : aOther.mScrollWithPage.WasPassed() ?
246 0 : aOther.mScrollWithPage.Value() : false, aList)
247 : , mDeviceId(&T::mDeviceId, "deviceId", aOther.mDeviceId, advanced, aList)
248 : , mViewportOffsetX(&T::mViewportOffsetX, "viewportOffsetX",
249 : aOther.mViewportOffsetX, advanced, aList)
250 : , mViewportOffsetY(&T::mViewportOffsetY, "viewportOffsetY",
251 : aOther.mViewportOffsetY, advanced, aList)
252 : , mViewportWidth(&T::mViewportWidth, "viewportWidth",
253 : aOther.mViewportWidth, advanced, aList)
254 : , mViewportHeight(&T::mViewportHeight, "viewportHeight",
255 : aOther.mViewportHeight, advanced, aList)
256 : , mEchoCancellation(&T::mEchoCancellation, "echoCancellation",
257 : aOther.mEchoCancellation, advanced, aList)
258 : , mNoiseSuppression(&T::mNoiseSuppression, "noiseSuppression",
259 : aOther.mNoiseSuppression,
260 : advanced, aList)
261 : , mAutoGainControl(&T::mAutoGainControl, "autoGainControl",
262 : aOther.mAutoGainControl, advanced, aList)
263 : , mChannelCount(&T::mChannelCount, "channelCount",
264 0 : aOther.mChannelCount, advanced, aList) {}
265 : };
266 :
267 : template<> bool NormalizedConstraintSet::Range<bool>::Merge(const Range& aOther);
268 : template<> void NormalizedConstraintSet::Range<bool>::FinalizeMerge();
269 :
270 : // Used instead of MediaTrackConstraints in lower-level code.
271 0 : struct NormalizedConstraints : public NormalizedConstraintSet
272 : {
273 : explicit NormalizedConstraints(const dom::MediaTrackConstraints& aOther,
274 : nsTArray<MemberPtrType>* aList = nullptr);
275 :
276 : // Merge constructor
277 : explicit NormalizedConstraints(
278 : const nsTArray<const NormalizedConstraints*>& aOthers);
279 :
280 : std::vector<NormalizedConstraintSet> mAdvanced;
281 : const char* mBadConstraint;
282 : };
283 :
284 : // Flattened version is used in low-level code with orthogonal constraints only.
285 0 : struct FlattenedConstraints : public NormalizedConstraintSet
286 : {
287 : explicit FlattenedConstraints(const NormalizedConstraints& aOther);
288 :
289 0 : explicit FlattenedConstraints(const dom::MediaTrackConstraints& aOther)
290 0 : : FlattenedConstraints(NormalizedConstraints(aOther)) {}
291 : };
292 :
293 : // A helper class for MediaEngines
294 :
295 0 : class MediaConstraintsHelper
296 : {
297 : protected:
298 : template<class ValueType, class NormalizedRange>
299 : static uint32_t FitnessDistance(ValueType aN, const NormalizedRange& aRange);
300 : static uint32_t FitnessDistance(nsString aN,
301 : const NormalizedConstraintSet::StringRange& aConstraint);
302 :
303 : static uint32_t
304 : GetMinimumFitnessDistance(const NormalizedConstraintSet &aConstraints,
305 : const nsString& aDeviceId);
306 :
307 : template<class DeviceType>
308 : static bool
309 0 : SomeSettingsFit(const NormalizedConstraints &aConstraints,
310 : nsTArray<RefPtr<DeviceType>>& aDevices)
311 : {
312 0 : nsTArray<const NormalizedConstraintSet*> sets;
313 0 : sets.AppendElement(&aConstraints);
314 :
315 0 : MOZ_ASSERT(aDevices.Length());
316 0 : for (auto& device : aDevices) {
317 0 : if (device->GetBestFitnessDistance(sets, false) != UINT32_MAX) {
318 0 : return true;
319 : }
320 : }
321 0 : return false;
322 : }
323 :
324 : public:
325 : // Apply constrains to a supplied list of devices (removes items from the list)
326 :
327 : template<class DeviceType>
328 : static const char*
329 0 : SelectSettings(const NormalizedConstraints &aConstraints,
330 : nsTArray<RefPtr<DeviceType>>& aDevices,
331 : bool aIsChrome)
332 : {
333 0 : auto& c = aConstraints;
334 :
335 : // First apply top-level constraints.
336 :
337 : // Stack constraintSets that pass, starting with the required one, because the
338 : // whole stack must be re-satisfied each time a capability-set is ruled out
339 : // (this avoids storing state or pushing algorithm into the lower-level code).
340 0 : nsTArray<RefPtr<DeviceType>> unsatisfactory;
341 0 : nsTArray<const NormalizedConstraintSet*> aggregateConstraints;
342 0 : aggregateConstraints.AppendElement(&c);
343 :
344 0 : std::multimap<uint32_t, RefPtr<DeviceType>> ordered;
345 :
346 0 : for (uint32_t i = 0; i < aDevices.Length();) {
347 0 : uint32_t distance = aDevices[i]->GetBestFitnessDistance(aggregateConstraints,
348 0 : aIsChrome);
349 0 : if (distance == UINT32_MAX) {
350 0 : unsatisfactory.AppendElement(aDevices[i]);
351 0 : aDevices.RemoveElementAt(i);
352 : } else {
353 0 : ordered.insert(std::pair<uint32_t, RefPtr<DeviceType>>(distance,
354 0 : aDevices[i]));
355 0 : ++i;
356 : }
357 : }
358 0 : if (!aDevices.Length()) {
359 0 : return FindBadConstraint(c, unsatisfactory);
360 : }
361 :
362 : // Order devices by shortest distance
363 0 : for (auto& ordinal : ordered) {
364 0 : aDevices.RemoveElement(ordinal.second);
365 0 : aDevices.AppendElement(ordinal.second);
366 : }
367 :
368 : // Then apply advanced constraints.
369 :
370 0 : for (int i = 0; i < int(c.mAdvanced.size()); i++) {
371 0 : aggregateConstraints.AppendElement(&c.mAdvanced[i]);
372 0 : nsTArray<RefPtr<DeviceType>> rejects;
373 0 : for (uint32_t j = 0; j < aDevices.Length();) {
374 0 : if (aDevices[j]->GetBestFitnessDistance(aggregateConstraints,
375 0 : aIsChrome) == UINT32_MAX) {
376 0 : rejects.AppendElement(aDevices[j]);
377 0 : aDevices.RemoveElementAt(j);
378 : } else {
379 0 : ++j;
380 : }
381 : }
382 0 : if (!aDevices.Length()) {
383 0 : aDevices.AppendElements(Move(rejects));
384 0 : aggregateConstraints.RemoveElementAt(aggregateConstraints.Length() - 1);
385 : }
386 : }
387 0 : return nullptr;
388 : }
389 :
390 : template<class DeviceType>
391 : static const char*
392 0 : FindBadConstraint(const NormalizedConstraints& aConstraints,
393 : nsTArray<RefPtr<DeviceType>>& aDevices)
394 : {
395 : // The spec says to report a constraint that satisfies NONE
396 : // of the sources. Unfortunately, this is a bit laborious to find out, and
397 : // requires updating as new constraints are added!
398 0 : auto& c = aConstraints;
399 0 : dom::MediaTrackConstraints empty;
400 :
401 0 : if (!aDevices.Length() ||
402 0 : !SomeSettingsFit(NormalizedConstraints(empty), aDevices)) {
403 0 : return "";
404 : }
405 : {
406 0 : NormalizedConstraints fresh(empty);
407 0 : fresh.mDeviceId = c.mDeviceId;
408 0 : if (!SomeSettingsFit(fresh, aDevices)) {
409 0 : return "deviceId";
410 : }
411 : }
412 : {
413 0 : NormalizedConstraints fresh(empty);
414 0 : fresh.mWidth = c.mWidth;
415 0 : if (!SomeSettingsFit(fresh, aDevices)) {
416 0 : return "width";
417 : }
418 : }
419 : {
420 0 : NormalizedConstraints fresh(empty);
421 0 : fresh.mHeight = c.mHeight;
422 0 : if (!SomeSettingsFit(fresh, aDevices)) {
423 0 : return "height";
424 : }
425 : }
426 : {
427 0 : NormalizedConstraints fresh(empty);
428 0 : fresh.mFrameRate = c.mFrameRate;
429 0 : if (!SomeSettingsFit(fresh, aDevices)) {
430 0 : return "frameRate";
431 : }
432 : }
433 : {
434 0 : NormalizedConstraints fresh(empty);
435 0 : fresh.mFacingMode = c.mFacingMode;
436 0 : if (!SomeSettingsFit(fresh, aDevices)) {
437 0 : return "facingMode";
438 : }
439 : }
440 0 : return "";
441 : }
442 :
443 : template<class MediaEngineSourceType>
444 : static const char*
445 : FindBadConstraint(const NormalizedConstraints& aConstraints,
446 : const MediaEngineSourceType& aMediaEngineSource,
447 : const nsString& aDeviceId);
448 :
449 : // Warn on and convert use of deprecated constraints to new ones
450 :
451 : static void
452 : ConvertOldWithWarning(
453 : const dom::OwningBooleanOrConstrainBooleanParameters& old,
454 : dom::OwningBooleanOrConstrainBooleanParameters& to,
455 : const char* aMessageName,
456 : nsPIDOMWindowInner* aWindow);
457 : };
458 :
459 : } // namespace mozilla
460 :
461 : #endif /* MEDIATRACKCONSTRAINTS_H_ */
|