Bug 1213517 - Add a way to merge multiple NormalizedConstraints. draft
authorJan-Ivar Bruaroey <jib@mozilla.com>
Sat, 02 Jul 2016 00:16:37 -0400
changeset 388771 ef006cf5f14530bc12cda2d59182539607f9cbe1
parent 388770 d22f14b07fa02cfab36877147c6761eb7dcf5ccb
child 388772 188b810e8d5c40e787dfde2326530780ae948e7e
push id23232
push userjbruaroey@mozilla.com
push dateSun, 17 Jul 2016 21:00:46 +0000
bugs1213517
milestone50.0a1
Bug 1213517 - Add a way to merge multiple NormalizedConstraints. MozReview-Commit-ID: LfB9QviCNxU
dom/media/webrtc/MediaTrackConstraints.cpp
dom/media/webrtc/MediaTrackConstraints.h
--- a/dom/media/webrtc/MediaTrackConstraints.cpp
+++ b/dom/media/webrtc/MediaTrackConstraints.cpp
@@ -25,16 +25,59 @@ NormalizedConstraintSet::Range<ValueType
       mMin = aOther.mMin.Value();
     }
     if (aOther.mMax.WasPassed()) {
       mMax = aOther.mMax.Value();
     }
   }
 }
 
+// The Range code works surprisingly well for bool, except when averaging ideals.
+template<>
+bool
+NormalizedConstraintSet::Range<bool>::Merge(const Range& aOther) {
+  if (!Intersects(aOther)) {
+    return false;
+  }
+  Intersect(aOther);
+
+  // To avoid "unsafe use of type 'bool'", we keep counter in mMergeDenominator
+  uint32_t counter = mMergeDenominator >> 16;
+  uint32_t denominator = mMergeDenominator & 0xffff;
+
+  if (aOther.mIdeal.isSome()) {
+    if (mIdeal.isNothing()) {
+      mIdeal.emplace(aOther.mIdeal.value());
+      counter = aOther.mIdeal.value();
+      denominator = 1;
+    } else {
+      if (!denominator) {
+        counter = mIdeal.value();
+      }
+      counter += aOther.mIdeal.value();
+      denominator = std::max(2U, denominator + 1);
+    }
+  }
+  mMergeDenominator = ((counter & 0xffff) << 16) + (denominator & 0xffff);
+  return true;
+}
+
+template<>
+void
+NormalizedConstraintSet::Range<bool>::FinalizeMerge()
+{
+  if (mMergeDenominator) {
+    uint32_t counter = mMergeDenominator >> 16;
+    uint32_t denominator = mMergeDenominator & 0xffff;
+
+    *mIdeal = !!(counter / denominator);
+    mMergeDenominator = 0;
+  }
+}
+
 NormalizedConstraintSet::LongRange::LongRange(
     const dom::OwningLongOrConstrainLongRange& aOther, bool advanced)
 : Range<int32_t>(1 + INT32_MIN, INT32_MAX) // +1 avoids Windows compiler bug
 {
   if (aOther.IsLong()) {
     if (advanced) {
       mMin = mMax = aOther.GetAsLong();
     } else {
@@ -174,25 +217,71 @@ NormalizedConstraintSet::StringRange::In
   for (auto& entry : mExact) {
     if (aOther.mExact.find(entry) == aOther.mExact.end()) {
       mExact.erase(entry);
     }
   }
 }
 
 NormalizedConstraints::NormalizedConstraints(const dom::MediaTrackConstraints& aOther)
-: NormalizedConstraintSet(aOther, false)
+: NormalizedConstraintSet(aOther, false), mOverconstrained(false)
 {
   if (aOther.mAdvanced.WasPassed()) {
     for (auto& entry : aOther.mAdvanced.Value()) {
       mAdvanced.AppendElement(NormalizedConstraintSet(entry, true));
     }
   }
 }
 
+// Merge constructor. Create net constraints out of merging a set of others.
+
+NormalizedConstraints::NormalizedConstraints(
+    const nsTArray<const NormalizedConstraints*>& aOthers)
+  : NormalizedConstraintSet(*aOthers[0])
+  , mOverconstrained(false)
+{
+  // Do intersection of all required constraints, and average of ideals.
+
+  for (uint32_t i = 1; i < aOthers.Length(); i++) {
+    auto& set = *aOthers[i];
+
+    if (!mWidth.Merge(set.mWidth) ||
+        !mHeight.Merge(set.mHeight) ||
+        !mFrameRate.Merge(set.mFrameRate) ||
+        !mFacingMode.Merge(set.mFacingMode) ||
+        mMediaSource != set.mMediaSource ||
+        mBrowserWindow != set.mBrowserWindow ||
+        !mViewportOffsetX.Merge(set.mViewportOffsetX) ||
+        !mViewportOffsetY.Merge(set.mViewportOffsetY) ||
+        !mViewportWidth.Merge(set.mViewportWidth) ||
+        !mViewportHeight.Merge(set.mViewportHeight) ||
+        !mEchoCancellation.Merge(set.mEchoCancellation) ||
+        !mMozNoiseSuppression.Merge(set.mMozNoiseSuppression) ||
+        !mMozAutoGainControl.Merge(set.mMozAutoGainControl)) {
+      mOverconstrained = true;
+      return;
+    }
+
+    for (auto& entry : set.mAdvanced) {
+      mAdvanced.AppendElement(entry);
+    }
+  }
+  mWidth.FinalizeMerge();
+  mHeight.FinalizeMerge();
+  mFrameRate.FinalizeMerge();
+  mFacingMode.FinalizeMerge();
+  mViewportOffsetX.FinalizeMerge();
+  mViewportOffsetY.FinalizeMerge();
+  mViewportWidth.FinalizeMerge();
+  mViewportHeight.FinalizeMerge();
+  mEchoCancellation.FinalizeMerge();
+  mMozNoiseSuppression.FinalizeMerge();
+  mMozAutoGainControl.FinalizeMerge();
+}
+
 FlattenedConstraints::FlattenedConstraints(const NormalizedConstraints& aOther)
 : NormalizedConstraintSet(aOther)
 {
   for (auto& set : aOther.mAdvanced) {
     // Must only apply compatible i.e. inherently non-overconstraining sets
     // This rule is pretty much why this code is centralized here.
     if (mWidth.Intersects(set.mWidth) &&
         mHeight.Intersects(set.mHeight) &&
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -34,37 +34,65 @@ static Enum StringToEnum(const EnumValue
 }
 
 // Helper classes for orthogonal constraints without interdependencies.
 // Instead of constraining values, constrain the constraints themselves.
 
 struct NormalizedConstraintSet
 {
   template<class ValueType>
-  struct Range
+  class Range
   {
+  public:
     ValueType mMin, mMax;
     Maybe<ValueType> mIdeal;
 
-    Range(ValueType aMin, ValueType aMax) : mMin(aMin), mMax(aMax) {}
+    Range(ValueType aMin, ValueType aMax)
+      : mMin(aMin), mMax(aMax), mMergeDenominator(0) {}
 
     template<class ConstrainRange>
     void SetFrom(const ConstrainRange& aOther);
     ValueType Clamp(ValueType n) const { return std::max(mMin, std::min(n, mMax)); }
     ValueType Get(ValueType defaultValue) const {
       return Clamp(mIdeal.valueOr(defaultValue));
     }
     bool Intersects(const Range& aOther) const {
       return mMax >= aOther.mMin && mMin <= aOther.mMax;
     }
     void Intersect(const Range& aOther) {
       MOZ_ASSERT(Intersects(aOther));
       mMin = std::max(mMin, aOther.mMin);
       mMax = std::min(mMax, aOther.mMax);
     }
+    bool Merge(const Range& aOther) {
+      if (!Intersects(aOther)) {
+        return false;
+      }
+      Intersect(aOther);
+
+      if (aOther.mIdeal.isSome()) {
+        if (mIdeal.isNothing()) {
+          mIdeal.emplace(aOther.mIdeal.value());
+          mMergeDenominator = 1;
+        } else {
+          *mIdeal += aOther.mIdeal.value();
+          mMergeDenominator = std::max(2U, mMergeDenominator + 1);
+        }
+      }
+      return true;
+    }
+    void FinalizeMerge()
+    {
+      if (mMergeDenominator) {
+        *mIdeal /= mMergeDenominator;
+        mMergeDenominator = 0;
+      }
+    }
+  private:
+    uint32_t mMergeDenominator;
   };
 
   struct LongRange : public Range<int32_t>
   {
     LongRange(const dom::OwningLongOrConstrainLongRange& aOther, bool advanced);
   };
 
   struct DoubleRange : public Range<double>
@@ -90,16 +118,31 @@ struct NormalizedConstraintSet
 
     void SetFrom(const dom::ConstrainDOMStringParameters& aOther);
     ValueType Clamp(const ValueType& n) const;
     ValueType Get(const ValueType& defaultValue) const {
       return Clamp(mIdeal.size() ? mIdeal : defaultValue);
     }
     bool Intersects(const StringRange& aOther) const;
     void Intersect(const StringRange& aOther);
+    bool Merge(const StringRange& aOther)
+    {
+      if (!Intersects(aOther)) {
+        return false;
+      }
+      Intersect(aOther);
+
+      for (auto& entry : aOther.mIdeal) {
+        if (mIdeal.find(entry) == mIdeal.end()) {
+          mIdeal.insert(entry);
+        }
+      }
+      return true;
+    }
+    void FinalizeMerge() {}
   };
 
   // All new constraints should be added here whether they use flattening or not
   LongRange mWidth, mHeight;
   DoubleRange mFrameRate;
   StringRange mFacingMode;
   nsString mMediaSource;
   long long mBrowserWindow;
@@ -124,21 +167,28 @@ struct NormalizedConstraintSet
   , mViewportOffsetY(aOther.mViewportOffsetY, advanced)
   , mViewportWidth(aOther.mViewportWidth, advanced)
   , mViewportHeight(aOther.mViewportHeight, advanced)
   , mEchoCancellation(aOther.mEchoCancellation, advanced)
   , mMozNoiseSuppression(aOther.mMozNoiseSuppression, advanced)
   , mMozAutoGainControl(aOther.mMozAutoGainControl, advanced) {}
 };
 
+template<> bool NormalizedConstraintSet::Range<bool>::Merge(const Range& aOther);
+template<> void NormalizedConstraintSet::Range<bool>::FinalizeMerge();
+
 // Used instead of MediaTrackConstraints in lower-level code.
 struct NormalizedConstraints : public NormalizedConstraintSet
 {
   explicit NormalizedConstraints(const dom::MediaTrackConstraints& aOther);
+  explicit NormalizedConstraints(
+      const nsTArray<const NormalizedConstraints*>& aOthers);
+
   nsTArray<NormalizedConstraintSet> mAdvanced;
+  bool mOverconstrained;
 };
 
 // Flattened version is used in low-level code with orthogonal constraints only.
 struct FlattenedConstraints : public NormalizedConstraintSet
 {
   explicit FlattenedConstraints(const NormalizedConstraints& aOther);
 
   explicit FlattenedConstraints(const dom::MediaTrackConstraints& aOther)