MediaTrackConstraints.h (13244B)
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 <set> 11 #include <vector> 12 13 #include "ipc/IPCMessageUtilsSpecializations.h" 14 #include "mozilla/dom/MediaStreamTrackBinding.h" 15 16 namespace mozilla { 17 18 class LocalMediaDevice; 19 class MediaDevice; 20 class MediaEnginePrefs; 21 22 // Helper classes for orthogonal constraints without interdependencies. 23 // Instead of constraining values, constrain the constraints themselves. 24 class NormalizedConstraintSet { 25 protected: 26 class BaseRange { 27 protected: 28 BaseRange(const nsCString& aName) : mName(aName) {} 29 virtual ~BaseRange() = default; 30 31 public: 32 bool operator==(const BaseRange& aOther) const noexcept { 33 return mName == aOther.mName; 34 } 35 BaseRange& operator=(const BaseRange& aOther) { 36 // We want all members assignable except mName. This allows e.g. 37 // std::swap(c.mWidth, c.mHeight) without names going out of sync. 38 return *this; 39 } 40 virtual bool Merge(const BaseRange& aOther) = 0; 41 virtual void FinalizeMerge() = 0; 42 43 const nsCString mName; 44 }; 45 46 public: 47 template <class ValueType> 48 class Range : public BaseRange { 49 public: 50 ValueType mMin, mMax; 51 Maybe<ValueType> mIdeal; 52 53 Range(const nsCString& aName, ValueType aMin, ValueType aMax) 54 : BaseRange(aName), mMin(aMin), mMax(aMax), mMergeDenominator(0) {} 55 virtual ~Range() = default; 56 57 bool operator==(const Range& aOther) const noexcept { 58 return BaseRange::operator==(aOther) && mMin == aOther.mMin && 59 mMax == aOther.mMax && mIdeal == aOther.mIdeal; 60 } 61 62 template <class ConstrainRange> 63 void SetFrom(const ConstrainRange& aOther); 64 65 /// Clamp n based on Range. If the Range is empty, mMin is returned. 66 ValueType Clamp(ValueType n) const { return std::clamp(n, mMin, mMax); } 67 ValueType Get(ValueType defaultValue) const { 68 return Clamp(mIdeal.valueOr(defaultValue)); 69 } 70 bool Intersects(const Range& aOther) const { 71 return mMax >= aOther.mMin && mMin <= aOther.mMax; 72 } 73 void Intersect(const Range& aOther) { 74 mMin = std::max(mMin, aOther.mMin); 75 if (Intersects(aOther)) { 76 mMax = std::min(mMax, aOther.mMax); 77 } else { 78 // If there is no intersection, we will down-scale or drop frame 79 mMax = std::max(mMax, aOther.mMax); 80 } 81 } 82 bool Merge(const Range& aOther) { 83 if (mName != "width" && mName != "height" && mName != "frameRate" && 84 !Intersects(aOther)) { 85 return false; 86 } 87 Intersect(aOther); 88 89 if (aOther.mIdeal.isSome()) { 90 // Ideal values, as stored, may be outside their min max range, so use 91 // clamped values in averaging, to avoid extreme outliers skewing 92 // results. 93 if (mIdeal.isNothing()) { 94 mIdeal.emplace(aOther.Get(0)); 95 mMergeDenominator = 1; 96 } else { 97 if (!mMergeDenominator) { 98 *mIdeal = Get(0); 99 mMergeDenominator = 1; 100 } 101 *mIdeal += aOther.Get(0); 102 mMergeDenominator++; 103 } 104 } 105 return true; 106 } 107 void FinalizeMerge() override { 108 if (mMergeDenominator) { 109 *mIdeal /= mMergeDenominator; 110 mMergeDenominator = 0; 111 } 112 } 113 void TakeHighestIdeal(const Range& aOther) { 114 if (aOther.mIdeal.isSome()) { 115 if (mIdeal.isNothing()) { 116 mIdeal.emplace(aOther.Get(0)); 117 } else { 118 *mIdeal = std::max(Get(0), aOther.Get(0)); 119 } 120 } 121 } 122 123 private: 124 bool Merge(const BaseRange& aOther) override { 125 return Merge(static_cast<const Range&>(aOther)); 126 } 127 128 uint32_t mMergeDenominator; 129 }; 130 131 struct LongRange final : public Range<int32_t> { 132 LongRange(const nsCString& aName, 133 const dom::Optional<dom::OwningLongOrConstrainLongRange>& aOther, 134 bool advanced); 135 }; 136 137 struct LongLongRange final : public Range<int64_t> { 138 LongLongRange(const nsCString& aName, const dom::Optional<int64_t>& aOther); 139 }; 140 141 struct DoubleRange final : public Range<double> { 142 DoubleRange( 143 const nsCString& aName, 144 const dom::Optional<dom::OwningDoubleOrConstrainDoubleRange>& aOther, 145 bool advanced); 146 }; 147 148 struct BooleanRange final : public Range<bool> { 149 BooleanRange( 150 const nsCString& aName, 151 const dom::Optional<dom::OwningBooleanOrConstrainBooleanParameters>& 152 aOther, 153 bool advanced); 154 155 BooleanRange(const nsCString& aName, const bool& aOther) 156 : Range<bool>(aName, false, true) { 157 mIdeal.emplace(aOther); 158 } 159 }; 160 161 struct StringRange final : public BaseRange { 162 using ValueType = std::set<nsString>; 163 ValueType mExact, mIdeal; 164 165 StringRange( 166 const nsCString& aName, 167 const dom::Optional< 168 dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters>& 169 aOther, 170 bool advanced); 171 172 StringRange(const nsCString& aName, const dom::Optional<nsString>& aOther) 173 : BaseRange(aName) { 174 if (aOther.WasPassed()) { 175 mIdeal.insert(aOther.Value()); 176 } 177 } 178 179 ~StringRange() = default; 180 181 bool operator==(const StringRange& aOther) const noexcept { 182 return BaseRange::operator==(aOther) && mExact == aOther.mExact && 183 mIdeal == aOther.mIdeal; 184 } 185 186 void SetFrom(const dom::ConstrainDOMStringParameters& aOther); 187 ValueType Clamp(const ValueType& n) const; 188 ValueType Get(const ValueType& defaultValue) const { 189 return Clamp(mIdeal.empty() ? defaultValue : mIdeal); 190 } 191 bool Intersects(const StringRange& aOther) const; 192 void Intersect(const StringRange& aOther); 193 bool Merge(const StringRange& aOther); 194 void FinalizeMerge() override {} 195 196 private: 197 bool Merge(const BaseRange& aOther) override { 198 return Merge(static_cast<const StringRange&>(aOther)); 199 } 200 }; 201 202 // All new constraints should be added here whether they use flattening or not 203 LongRange mWidth, mHeight; 204 DoubleRange mFrameRate; 205 StringRange mFacingMode; 206 StringRange mResizeMode; 207 StringRange mMediaSource; 208 LongLongRange mBrowserWindow; 209 StringRange mDeviceId; 210 StringRange mGroupId; 211 LongRange mViewportOffsetX, mViewportOffsetY, mViewportWidth, mViewportHeight; 212 BooleanRange mEchoCancellation, mNoiseSuppression, mAutoGainControl; 213 LongRange mChannelCount; 214 215 public: 216 NormalizedConstraintSet() 217 : NormalizedConstraintSet(dom::MediaTrackConstraintSet(), 218 /* advanced = */ false) {} 219 220 NormalizedConstraintSet(const dom::MediaTrackConstraintSet& aOther, 221 bool advanced) 222 : mWidth("width"_ns, aOther.mWidth, advanced), 223 mHeight("height"_ns, aOther.mHeight, advanced), 224 mFrameRate("frameRate"_ns, aOther.mFrameRate, advanced), 225 mFacingMode("facingMode"_ns, aOther.mFacingMode, advanced), 226 mResizeMode("resizeMode"_ns, aOther.mResizeMode, advanced), 227 mMediaSource("mediaSource"_ns, aOther.mMediaSource), 228 mBrowserWindow("browserWindow"_ns, aOther.mBrowserWindow), 229 mDeviceId("deviceId"_ns, aOther.mDeviceId, advanced), 230 mGroupId("groupId"_ns, aOther.mGroupId, advanced), 231 mViewportOffsetX("viewportOffsetX"_ns, aOther.mViewportOffsetX, 232 advanced), 233 mViewportOffsetY("viewportOffsetY"_ns, aOther.mViewportOffsetY, 234 advanced), 235 mViewportWidth("viewportWidth"_ns, aOther.mViewportWidth, advanced), 236 mViewportHeight("viewportHeight"_ns, aOther.mViewportHeight, advanced), 237 mEchoCancellation("echoCancellation"_ns, aOther.mEchoCancellation, 238 advanced), 239 mNoiseSuppression("noiseSuppression"_ns, aOther.mNoiseSuppression, 240 advanced), 241 mAutoGainControl("autoGainControl"_ns, aOther.mAutoGainControl, 242 advanced), 243 mChannelCount("channelCount"_ns, aOther.mChannelCount, advanced) {} 244 245 bool operator==(const NormalizedConstraintSet& aOther) const noexcept { 246 return mWidth == aOther.mWidth && mHeight == aOther.mHeight && 247 mFrameRate == aOther.mFrameRate && 248 mFacingMode == aOther.mFacingMode && 249 mResizeMode == aOther.mResizeMode && 250 mMediaSource == aOther.mMediaSource && 251 mBrowserWindow == aOther.mBrowserWindow && 252 mDeviceId == aOther.mDeviceId && mGroupId == aOther.mGroupId && 253 mViewportOffsetX == aOther.mViewportOffsetX && 254 mViewportOffsetY == aOther.mViewportOffsetY && 255 mViewportWidth == aOther.mViewportWidth && 256 mViewportHeight == aOther.mViewportHeight && 257 mEchoCancellation == aOther.mEchoCancellation && 258 mNoiseSuppression == aOther.mNoiseSuppression && 259 mAutoGainControl == aOther.mAutoGainControl && 260 mChannelCount == aOther.mChannelCount; 261 } 262 }; 263 264 template <> 265 bool NormalizedConstraintSet::Range<bool>::Merge(const Range& aOther); 266 template <> 267 void NormalizedConstraintSet::Range<bool>::FinalizeMerge(); 268 269 // Used instead of MediaTrackConstraints in lower-level code. 270 struct NormalizedConstraints : public NormalizedConstraintSet { 271 NormalizedConstraints() = default; 272 explicit NormalizedConstraints(const dom::MediaTrackConstraints& aOther); 273 274 bool operator==(const NormalizedConstraints& aOther) const noexcept { 275 return NormalizedConstraintSet::operator==(aOther) && 276 mAdvanced == aOther.mAdvanced; 277 } 278 279 bool operator!=(const NormalizedConstraints& aOther) const noexcept { 280 return !(*this == aOther); 281 } 282 283 std::vector<NormalizedConstraintSet> mAdvanced; 284 }; 285 286 // Flattened version is used in low-level code with orthogonal constraints only. 287 struct FlattenedConstraints : public NormalizedConstraintSet { 288 FlattenedConstraints() = default; 289 explicit FlattenedConstraints(const NormalizedConstraints& aOther); 290 explicit FlattenedConstraints(const dom::MediaTrackConstraints& aOther) 291 : FlattenedConstraints(NormalizedConstraints(aOther)) {} 292 293 bool operator==(const FlattenedConstraints& aOther) const noexcept { 294 return NormalizedConstraintSet::operator==(aOther); 295 } 296 297 bool operator!=(const FlattenedConstraints& aOther) const noexcept { 298 return !(*this == aOther); 299 } 300 }; 301 302 // A helper class for MediaEngineSources 303 class MediaConstraintsHelper { 304 public: 305 template <class ValueType, class NormalizedRange> 306 static uint32_t FitnessDistance(ValueType aN, const NormalizedRange& aRange) { 307 if (aRange.mMin > aN || aRange.mMax < aN) { 308 return UINT32_MAX; 309 } 310 if (aN == aRange.mIdeal.valueOr(aN)) { 311 return 0; 312 } 313 return uint32_t( 314 ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) / 315 std::max(std::abs(aN), std::abs(aRange.mIdeal.value())))); 316 } 317 318 template <class ValueType, class NormalizedRange> 319 static uint32_t FeasibilityDistance(ValueType aN, 320 const NormalizedRange& aRange) { 321 if (aRange.mMin > aN) { 322 return UINT32_MAX; 323 } 324 // We prefer larger resolution because now we support downscaling 325 if (aN == aRange.mIdeal.valueOr(aN)) { 326 return 0; 327 } 328 329 if (aN > aRange.mIdeal.value()) { 330 return uint32_t( 331 ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) / 332 std::max(std::abs(aN), std::abs(aRange.mIdeal.value())))); 333 } 334 335 return 10000 + 336 uint32_t(ValueType( 337 (std::abs(aN - aRange.mIdeal.value()) * 1000) / 338 std::max(std::abs(aN), std::abs(aRange.mIdeal.value())))); 339 } 340 341 static uint32_t FitnessDistance( 342 const Maybe<nsString>& aN, 343 const NormalizedConstraintSet::StringRange& aParams); 344 345 protected: 346 static bool SomeSettingsFit( 347 const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs, 348 const nsTArray<RefPtr<LocalMediaDevice>>& aDevices); 349 350 public: 351 static uint32_t GetMinimumFitnessDistance( 352 const NormalizedConstraintSet& aConstraints, const nsString& aDeviceId, 353 const nsString& aGroupId); 354 355 // Apply constrains to a supplied list of devices (removes items from the 356 // list) 357 static const char* SelectSettings( 358 const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs, 359 nsTArray<RefPtr<LocalMediaDevice>>& aDevices, 360 dom::CallerType aCallerType); 361 362 static const char* FindBadConstraint( 363 const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs, 364 const nsTArray<RefPtr<LocalMediaDevice>>& aDevices); 365 366 static const char* FindBadConstraint( 367 const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs, 368 const MediaDevice* aMediaDevice); 369 370 static void LogConstraints(const NormalizedConstraintSet& aConstraints); 371 372 static Maybe<dom::VideoResizeModeEnum> GetResizeMode( 373 const NormalizedConstraintSet& aConstraints, 374 const MediaEnginePrefs& aPrefs); 375 }; 376 377 } // namespace mozilla 378 379 #endif /* MEDIATRACKCONSTRAINTS_H_ */