Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
// This file should not be included by other includes, as it contains code
#ifndef MEDIATRACKCONSTRAINTS_H_
#define MEDIATRACKCONSTRAINTS_H_
#include <map>
#include <set>
#include <vector>
#include "mozilla/Attributes.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/MediaTrackSupportedConstraintsBinding.h"
namespace mozilla {
class LocalMediaDevice;
class MediaDevice;
// Helper classes for orthogonal constraints without interdependencies.
// Instead of constraining values, constrain the constraints themselves.
class NormalizedConstraintSet {
protected:
class BaseRange {
protected:
typedef BaseRange NormalizedConstraintSet::*MemberPtrType;
BaseRange(MemberPtrType aMemberPtr, const char* aName,
nsTArray<MemberPtrType>* aList)
: mName(aName) {
if (aList) {
aList->AppendElement(aMemberPtr);
}
}
virtual ~BaseRange() = default;
public:
virtual bool Merge(const BaseRange& aOther) = 0;
virtual void FinalizeMerge() = 0;
const char* mName;
};
typedef BaseRange NormalizedConstraintSet::*MemberPtrType;
public:
template <class ValueType>
class Range : public BaseRange {
public:
ValueType mMin, mMax;
Maybe<ValueType> mIdeal;
Range(MemberPtrType aMemberPtr, const char* aName, ValueType aMin,
ValueType aMax, nsTArray<MemberPtrType>* aList)
: BaseRange(aMemberPtr, aName, aList),
mMin(aMin),
mMax(aMax),
mMergeDenominator(0) {}
virtual ~Range() = default;
template <class ConstrainRange>
void SetFrom(const ConstrainRange& aOther);
/// Clamp n based on Range. If the Range is empty, mMin is returned.
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) {
mMin = std::max(mMin, aOther.mMin);
if (Intersects(aOther)) {
mMax = std::min(mMax, aOther.mMax);
} else {
// If there is no intersection, we will down-scale or drop frame
mMax = std::max(mMax, aOther.mMax);
}
}
bool Merge(const Range& aOther) {
if (strcmp(mName, "width") != 0 && strcmp(mName, "height") != 0 &&
strcmp(mName, "frameRate") != 0 && !Intersects(aOther)) {
return false;
}
Intersect(aOther);
if (aOther.mIdeal.isSome()) {
// Ideal values, as stored, may be outside their min max range, so use
// clamped values in averaging, to avoid extreme outliers skewing
// results.
if (mIdeal.isNothing()) {
mIdeal.emplace(aOther.Get(0));
mMergeDenominator = 1;
} else {
if (!mMergeDenominator) {
*mIdeal = Get(0);
mMergeDenominator = 1;
}
*mIdeal += aOther.Get(0);
mMergeDenominator++;
}
}
return true;
}
void FinalizeMerge() override {
if (mMergeDenominator) {
*mIdeal /= mMergeDenominator;
mMergeDenominator = 0;
}
}
void TakeHighestIdeal(const Range& aOther) {
if (aOther.mIdeal.isSome()) {
if (mIdeal.isNothing()) {
mIdeal.emplace(aOther.Get(0));
} else {
*mIdeal = std::max(Get(0), aOther.Get(0));
}
}
}
private:
bool Merge(const BaseRange& aOther) override {
return Merge(static_cast<const Range&>(aOther));
}
uint32_t mMergeDenominator;
};
struct LongRange : public Range<int32_t> {
typedef LongRange NormalizedConstraintSet::*LongPtrType;
LongRange(LongPtrType aMemberPtr, const char* aName,
const dom::Optional<dom::OwningLongOrConstrainLongRange>& aOther,
bool advanced, nsTArray<MemberPtrType>* aList);
};
struct LongLongRange : public Range<int64_t> {
typedef LongLongRange NormalizedConstraintSet::*LongLongPtrType;
LongLongRange(LongLongPtrType aMemberPtr, const char* aName,
const long long& aOther, nsTArray<MemberPtrType>* aList);
};
struct DoubleRange : public Range<double> {
typedef DoubleRange NormalizedConstraintSet::*DoublePtrType;
DoubleRange(
DoublePtrType aMemberPtr, const char* aName,
const dom::Optional<dom::OwningDoubleOrConstrainDoubleRange>& aOther,
bool advanced, nsTArray<MemberPtrType>* aList);
};
struct BooleanRange : public Range<bool> {
typedef BooleanRange NormalizedConstraintSet::*BooleanPtrType;
BooleanRange(
BooleanPtrType aMemberPtr, const char* aName,
const dom::Optional<dom::OwningBooleanOrConstrainBooleanParameters>&
aOther,
bool advanced, nsTArray<MemberPtrType>* aList);
BooleanRange(BooleanPtrType aMemberPtr, const char* aName,
const bool& aOther, nsTArray<MemberPtrType>* aList)
: Range<bool>((MemberPtrType)aMemberPtr, aName, false, true, aList) {
mIdeal.emplace(aOther);
}
};
struct StringRange : public BaseRange {
typedef std::set<nsString> ValueType;
ValueType mExact, mIdeal;
typedef StringRange NormalizedConstraintSet::*StringPtrType;
StringRange(
StringPtrType aMemberPtr, const char* aName,
const dom::Optional<
dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters>&
aOther,
bool advanced, nsTArray<MemberPtrType>* aList);
StringRange(StringPtrType aMemberPtr, const char* aName,
const dom::Optional<nsString>& aOther,
nsTArray<MemberPtrType>* aList)
: BaseRange((MemberPtrType)aMemberPtr, aName, aList) {
if (aOther.WasPassed()) {
mIdeal.insert(aOther.Value());
}
}
~StringRange() = default;
void SetFrom(const dom::ConstrainDOMStringParameters& aOther);
ValueType Clamp(const ValueType& n) const;
ValueType Get(const ValueType& defaultValue) const {
return Clamp(mIdeal.empty() ? defaultValue : mIdeal);
}
bool Intersects(const StringRange& aOther) const;
void Intersect(const StringRange& aOther);
bool Merge(const StringRange& aOther);
void FinalizeMerge() override {}
private:
bool Merge(const BaseRange& aOther) override {
return Merge(static_cast<const StringRange&>(aOther));
}
};
// All new constraints should be added here whether they use flattening or not
LongRange mWidth, mHeight;
DoubleRange mFrameRate;
StringRange mFacingMode;
StringRange mMediaSource;
LongLongRange mBrowserWindow;
StringRange mDeviceId;
StringRange mGroupId;
LongRange mViewportOffsetX, mViewportOffsetY, mViewportWidth, mViewportHeight;
BooleanRange mEchoCancellation, mNoiseSuppression, mAutoGainControl;
LongRange mChannelCount;
private:
typedef NormalizedConstraintSet T;
public:
NormalizedConstraintSet(const dom::MediaTrackConstraintSet& aOther,
bool advanced,
nsTArray<MemberPtrType>* aList = nullptr)
: mWidth(&T::mWidth, "width", aOther.mWidth, advanced, aList),
mHeight(&T::mHeight, "height", aOther.mHeight, advanced, aList),
mFrameRate(&T::mFrameRate, "frameRate", aOther.mFrameRate, advanced,
aList),
mFacingMode(&T::mFacingMode, "facingMode", aOther.mFacingMode, advanced,
aList),
mMediaSource(&T::mMediaSource, "mediaSource", aOther.mMediaSource,
aList),
mBrowserWindow(&T::mBrowserWindow, "browserWindow",
aOther.mBrowserWindow.WasPassed()
? aOther.mBrowserWindow.Value()
: 0,
aList),
mDeviceId(&T::mDeviceId, "deviceId", aOther.mDeviceId, advanced, aList),
mGroupId(&T::mGroupId, "groupId", aOther.mGroupId, advanced, aList),
mViewportOffsetX(&T::mViewportOffsetX, "viewportOffsetX",
aOther.mViewportOffsetX, advanced, aList),
mViewportOffsetY(&T::mViewportOffsetY, "viewportOffsetY",
aOther.mViewportOffsetY, advanced, aList),
mViewportWidth(&T::mViewportWidth, "viewportWidth",
aOther.mViewportWidth, advanced, aList),
mViewportHeight(&T::mViewportHeight, "viewportHeight",
aOther.mViewportHeight, advanced, aList),
mEchoCancellation(&T::mEchoCancellation, "echoCancellation",
aOther.mEchoCancellation, advanced, aList),
mNoiseSuppression(&T::mNoiseSuppression, "noiseSuppression",
aOther.mNoiseSuppression, advanced, aList),
mAutoGainControl(&T::mAutoGainControl, "autoGainControl",
aOther.mAutoGainControl, advanced, aList),
mChannelCount(&T::mChannelCount, "channelCount", aOther.mChannelCount,
advanced, aList) {}
};
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,
nsTArray<MemberPtrType>* aList = nullptr);
std::vector<NormalizedConstraintSet> mAdvanced;
const char* mBadConstraint;
};
// 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)
: FlattenedConstraints(NormalizedConstraints(aOther)) {}
};
// A helper class for MediaEngineSources
class MediaConstraintsHelper {
public:
template <class ValueType, class NormalizedRange>
static uint32_t FitnessDistance(ValueType aN, const NormalizedRange& aRange) {
if (aRange.mMin > aN || aRange.mMax < aN) {
return UINT32_MAX;
}
if (aN == aRange.mIdeal.valueOr(aN)) {
return 0;
}
return uint32_t(
ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) /
std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
}
template <class ValueType, class NormalizedRange>
static uint32_t FeasibilityDistance(ValueType aN,
const NormalizedRange& aRange) {
if (aRange.mMin > aN) {
return UINT32_MAX;
}
// We prefer larger resolution because now we support downscaling
if (aN == aRange.mIdeal.valueOr(aN)) {
return 0;
}
if (aN > aRange.mIdeal.value()) {
return uint32_t(
ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) /
std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
}
return 10000 +
uint32_t(ValueType(
(std::abs(aN - aRange.mIdeal.value()) * 1000) /
std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
}
static uint32_t FitnessDistance(
const Maybe<nsString>& aN,
const NormalizedConstraintSet::StringRange& aParams);
protected:
static bool SomeSettingsFit(
const NormalizedConstraints& aConstraints,
const nsTArray<RefPtr<LocalMediaDevice>>& aDevices);
public:
static uint32_t GetMinimumFitnessDistance(
const NormalizedConstraintSet& aConstraints, const nsString& aDeviceId,
const nsString& aGroupId);
// Apply constrains to a supplied list of devices (removes items from the
// list)
static const char* SelectSettings(
const NormalizedConstraints& aConstraints,
nsTArray<RefPtr<LocalMediaDevice>>& aDevices,
dom::CallerType aCallerType);
static const char* FindBadConstraint(
const NormalizedConstraints& aConstraints,
const nsTArray<RefPtr<LocalMediaDevice>>& aDevices);
static const char* FindBadConstraint(
const NormalizedConstraints& aConstraints,
const MediaDevice* aMediaDevice);
static void LogConstraints(const NormalizedConstraintSet& aConstraints);
};
} // namespace mozilla
#endif /* MEDIATRACKCONSTRAINTS_H_ */