Bug 1213517 - Add a way to merge multiple NormalizedConstraints.
MozReview-Commit-ID: LfB9QviCNxU
--- 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)