LCOV - code coverage report
Current view: top level - dom/media/webrtc - MediaTrackConstraints.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 228 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 28 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       4             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "MediaTrackConstraints.h"
       7             : #include "nsIScriptError.h"
       8             : #include "mozilla/dom/MediaStreamTrackBinding.h"
       9             : 
      10             : #include <limits>
      11             : #include <algorithm>
      12             : #include <iterator>
      13             : 
      14             : namespace mozilla {
      15             : 
      16             : using dom::ConstrainBooleanParameters;
      17             : using dom::OwningLongOrConstrainLongRange;
      18             : 
      19             : template<class ValueType>
      20             : template<class ConstrainRange>
      21             : void
      22           0 : NormalizedConstraintSet::Range<ValueType>::SetFrom(const ConstrainRange& aOther)
      23             : {
      24           0 :   if (aOther.mIdeal.WasPassed()) {
      25           0 :     mIdeal.emplace(aOther.mIdeal.Value());
      26             :   }
      27           0 :   if (aOther.mExact.WasPassed()) {
      28           0 :     mMin = aOther.mExact.Value();
      29           0 :     mMax = aOther.mExact.Value();
      30             :   } else {
      31           0 :     if (aOther.mMin.WasPassed()) {
      32           0 :       mMin = aOther.mMin.Value();
      33             :     }
      34           0 :     if (aOther.mMax.WasPassed()) {
      35           0 :       mMax = aOther.mMax.Value();
      36             :     }
      37             :   }
      38           0 : }
      39             : 
      40             : // The Range code works surprisingly well for bool, except when averaging ideals.
      41             : template<>
      42             : bool
      43           0 : NormalizedConstraintSet::Range<bool>::Merge(const Range& aOther) {
      44           0 :   if (!Intersects(aOther)) {
      45           0 :     return false;
      46             :   }
      47           0 :   Intersect(aOther);
      48             : 
      49             :   // To avoid "unsafe use of type 'bool'", we keep counter in mMergeDenominator
      50           0 :   uint32_t counter = mMergeDenominator >> 16;
      51           0 :   uint32_t denominator = mMergeDenominator & 0xffff;
      52             : 
      53           0 :   if (aOther.mIdeal.isSome()) {
      54           0 :     if (mIdeal.isNothing()) {
      55           0 :       mIdeal.emplace(aOther.Get(false));
      56           0 :       counter = aOther.Get(false);
      57           0 :       denominator = 1;
      58             :     } else {
      59           0 :       if (!denominator) {
      60           0 :         counter = Get(false);
      61           0 :         denominator = 1;
      62             :       }
      63           0 :       counter += aOther.Get(false);
      64           0 :       denominator++;
      65             :     }
      66             :   }
      67           0 :   mMergeDenominator = ((counter & 0xffff) << 16) + (denominator & 0xffff);
      68           0 :   return true;
      69             : }
      70             : 
      71             : template<>
      72             : void
      73           0 : NormalizedConstraintSet::Range<bool>::FinalizeMerge()
      74             : {
      75           0 :   if (mMergeDenominator) {
      76           0 :     uint32_t counter = mMergeDenominator >> 16;
      77           0 :     uint32_t denominator = mMergeDenominator & 0xffff;
      78             : 
      79           0 :     *mIdeal = !!(counter / denominator);
      80           0 :     mMergeDenominator = 0;
      81             :   }
      82           0 : }
      83             : 
      84           0 : NormalizedConstraintSet::LongRange::LongRange(
      85             :     LongPtrType aMemberPtr,
      86             :     const char* aName,
      87             :     const dom::OwningLongOrConstrainLongRange& aOther,
      88             :     bool advanced,
      89           0 :     nsTArray<MemberPtrType>* aList)
      90             : : Range<int32_t>((MemberPtrType)aMemberPtr, aName,
      91             :                  1 + INT32_MIN, INT32_MAX, // +1 avoids Windows compiler bug
      92           0 :                  aList)
      93             : {
      94           0 :   if (aOther.IsLong()) {
      95           0 :     if (advanced) {
      96           0 :       mMin = mMax = aOther.GetAsLong();
      97             :     } else {
      98           0 :       mIdeal.emplace(aOther.GetAsLong());
      99             :     }
     100             :   } else {
     101           0 :     SetFrom(aOther.GetAsConstrainLongRange());
     102             :   }
     103           0 : }
     104             : 
     105           0 : NormalizedConstraintSet::LongLongRange::LongLongRange(
     106             :     LongLongPtrType aMemberPtr,
     107             :     const char* aName,
     108             :     const long long& aOther,
     109           0 :     nsTArray<MemberPtrType>* aList)
     110             : : Range<int64_t>((MemberPtrType)aMemberPtr, aName,
     111             :                  1 + INT64_MIN, INT64_MAX, // +1 avoids Windows compiler bug
     112           0 :                  aList)
     113             : {
     114           0 :   mIdeal.emplace(aOther);
     115           0 : }
     116             : 
     117           0 : NormalizedConstraintSet::DoubleRange::DoubleRange(
     118             :     DoublePtrType aMemberPtr,
     119             :     const char* aName,
     120             :     const dom::OwningDoubleOrConstrainDoubleRange& aOther, bool advanced,
     121           0 :     nsTArray<MemberPtrType>* aList)
     122             : : Range<double>((MemberPtrType)aMemberPtr, aName,
     123           0 :                 -std::numeric_limits<double>::infinity(),
     124           0 :                 std::numeric_limits<double>::infinity(), aList)
     125             : {
     126           0 :   if (aOther.IsDouble()) {
     127           0 :     if (advanced) {
     128           0 :       mMin = mMax = aOther.GetAsDouble();
     129             :     } else {
     130           0 :       mIdeal.emplace(aOther.GetAsDouble());
     131             :     }
     132             :   } else {
     133           0 :     SetFrom(aOther.GetAsConstrainDoubleRange());
     134             :   }
     135           0 : }
     136             : 
     137           0 : NormalizedConstraintSet::BooleanRange::BooleanRange(
     138             :     BooleanPtrType aMemberPtr,
     139             :     const char* aName,
     140             :     const dom::OwningBooleanOrConstrainBooleanParameters& aOther,
     141             :     bool advanced,
     142           0 :     nsTArray<MemberPtrType>* aList)
     143           0 : : Range<bool>((MemberPtrType)aMemberPtr, aName, false, true, aList)
     144             : {
     145           0 :   if (aOther.IsBoolean()) {
     146           0 :     if (advanced) {
     147           0 :       mMin = mMax = aOther.GetAsBoolean();
     148             :     } else {
     149           0 :       mIdeal.emplace(aOther.GetAsBoolean());
     150             :     }
     151             :   } else {
     152           0 :     const dom::ConstrainBooleanParameters& r = aOther.GetAsConstrainBooleanParameters();
     153           0 :     if (r.mIdeal.WasPassed()) {
     154           0 :       mIdeal.emplace(r.mIdeal.Value());
     155             :     }
     156           0 :     if (r.mExact.WasPassed()) {
     157           0 :       mMin = r.mExact.Value();
     158           0 :       mMax = r.mExact.Value();
     159             :     }
     160             :   }
     161           0 : }
     162             : 
     163           0 : NormalizedConstraintSet::StringRange::StringRange(
     164             :     StringPtrType aMemberPtr,
     165             :     const char* aName,
     166             :     const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aOther,
     167             :     bool advanced,
     168           0 :     nsTArray<MemberPtrType>* aList)
     169           0 :   : BaseRange((MemberPtrType)aMemberPtr, aName, aList)
     170             : {
     171           0 :   if (aOther.IsString()) {
     172           0 :     if (advanced) {
     173           0 :       mExact.insert(aOther.GetAsString());
     174             :     } else {
     175           0 :       mIdeal.insert(aOther.GetAsString());
     176             :     }
     177           0 :   } else if (aOther.IsStringSequence()) {
     178           0 :     if (advanced) {
     179           0 :       mExact.clear();
     180           0 :       for (auto& str : aOther.GetAsStringSequence()) {
     181           0 :         mExact.insert(str);
     182             :       }
     183             :     } else {
     184           0 :       mIdeal.clear();
     185           0 :       for (auto& str : aOther.GetAsStringSequence()) {
     186           0 :         mIdeal.insert(str);
     187             :       }
     188             :     }
     189             :   } else {
     190           0 :     SetFrom(aOther.GetAsConstrainDOMStringParameters());
     191             :   }
     192           0 : }
     193             : 
     194             : void
     195           0 : NormalizedConstraintSet::StringRange::SetFrom(
     196             :     const dom::ConstrainDOMStringParameters& aOther)
     197             : {
     198           0 :   if (aOther.mIdeal.WasPassed()) {
     199           0 :     mIdeal.clear();
     200           0 :     if (aOther.mIdeal.Value().IsString()) {
     201           0 :       mIdeal.insert(aOther.mIdeal.Value().GetAsString());
     202             :     } else {
     203           0 :       for (auto& str : aOther.mIdeal.Value().GetAsStringSequence()) {
     204           0 :         mIdeal.insert(str);
     205             :       }
     206             :     }
     207             :   }
     208           0 :   if (aOther.mExact.WasPassed()) {
     209           0 :     mExact.clear();
     210           0 :     if (aOther.mExact.Value().IsString()) {
     211           0 :       mExact.insert(aOther.mExact.Value().GetAsString());
     212             :     } else {
     213           0 :       for (auto& str : aOther.mExact.Value().GetAsStringSequence()) {
     214           0 :         mIdeal.insert(str);
     215             :       }
     216             :     }
     217             :   }
     218           0 : }
     219             : 
     220             : auto
     221           0 : NormalizedConstraintSet::StringRange::Clamp(const ValueType& n) const -> ValueType
     222             : {
     223           0 :   if (!mExact.size()) {
     224           0 :     return n;
     225             :   }
     226           0 :   ValueType result;
     227           0 :   for (auto& entry : n) {
     228           0 :     if (mExact.find(entry) != mExact.end()) {
     229           0 :       result.insert(entry);
     230             :     }
     231             :   }
     232           0 :   return result;
     233             : }
     234             : 
     235             : bool
     236           0 : NormalizedConstraintSet::StringRange::Intersects(const StringRange& aOther) const
     237             : {
     238           0 :   if (!mExact.size() || !aOther.mExact.size()) {
     239           0 :     return true;
     240             :   }
     241             : 
     242           0 :   ValueType intersection;
     243             :   set_intersection(mExact.begin(), mExact.end(),
     244             :                    aOther.mExact.begin(), aOther.mExact.end(),
     245           0 :                    std::inserter(intersection, intersection.begin()));
     246           0 :   return !!intersection.size();
     247             : }
     248             : 
     249             : void
     250           0 : NormalizedConstraintSet::StringRange::Intersect(const StringRange& aOther)
     251             : {
     252           0 :   if (!aOther.mExact.size()) {
     253           0 :     return;
     254             :   }
     255             : 
     256           0 :   ValueType intersection;
     257             :   set_intersection(mExact.begin(), mExact.end(),
     258             :                    aOther.mExact.begin(), aOther.mExact.end(),
     259           0 :                    std::inserter(intersection, intersection.begin()));
     260           0 :   mExact = intersection;
     261             : }
     262             : 
     263             : bool
     264           0 : NormalizedConstraintSet::StringRange::Merge(const StringRange& aOther)
     265             : {
     266           0 :   if (!Intersects(aOther)) {
     267           0 :     return false;
     268             :   }
     269           0 :   Intersect(aOther);
     270             : 
     271           0 :   ValueType unioned;
     272             :   set_union(mIdeal.begin(), mIdeal.end(),
     273             :             aOther.mIdeal.begin(), aOther.mIdeal.end(),
     274           0 :             std::inserter(unioned, unioned.begin()));
     275           0 :   mIdeal = unioned;
     276           0 :   return true;
     277             : }
     278             : 
     279           0 : NormalizedConstraints::NormalizedConstraints(
     280             :     const dom::MediaTrackConstraints& aOther,
     281           0 :     nsTArray<MemberPtrType>* aList)
     282             :   : NormalizedConstraintSet(aOther, false, aList)
     283           0 :   , mBadConstraint(nullptr)
     284             : {
     285           0 :   if (aOther.mAdvanced.WasPassed()) {
     286           0 :     for (auto& entry : aOther.mAdvanced.Value()) {
     287           0 :       mAdvanced.push_back(NormalizedConstraintSet(entry, true));
     288             :     }
     289             :   }
     290           0 : }
     291             : 
     292             : // Merge constructor. Create net constraints out of merging a set of others.
     293             : // This is only used to resolve competing constraints from concurrent requests,
     294             : // something the spec doesn't cover.
     295             : 
     296           0 : NormalizedConstraints::NormalizedConstraints(
     297           0 :     const nsTArray<const NormalizedConstraints*>& aOthers)
     298           0 :   : NormalizedConstraintSet(*aOthers[0])
     299           0 :   , mBadConstraint(nullptr)
     300             : {
     301           0 :   for (auto& entry : aOthers[0]->mAdvanced) {
     302           0 :     mAdvanced.push_back(entry);
     303             :   }
     304             : 
     305             :   // Create a list of member pointers.
     306           0 :   nsTArray<MemberPtrType> list;
     307           0 :   NormalizedConstraints dummy(dom::MediaTrackConstraints(), &list);
     308             : 
     309             :   // Do intersection of all required constraints, and average of ideals,
     310             : 
     311           0 :   for (uint32_t i = 1; i < aOthers.Length(); i++) {
     312           0 :     auto& other = *aOthers[i];
     313             : 
     314           0 :     for (auto& memberPtr : list) {
     315           0 :       auto& member = this->*memberPtr;
     316           0 :       auto& otherMember = other.*memberPtr;
     317             : 
     318           0 :       if (!member.Merge(otherMember)) {
     319           0 :         mBadConstraint = member.mName;
     320           0 :         return;
     321             :       }
     322             :     }
     323             : 
     324           0 :     for (auto& entry : other.mAdvanced) {
     325           0 :       mAdvanced.push_back(entry);
     326             :     }
     327             :   }
     328           0 :   for (auto& memberPtr : list) {
     329           0 :     (this->*memberPtr).FinalizeMerge();
     330             :   }
     331             : 
     332             :   // ...except for resolution and frame rate where we take the highest ideal.
     333             :   // This is a bit of a hack based on the perception that people would be more
     334             :   // surprised if they were to get lower resolution than they ideally requested.
     335             :   //
     336             :   // The spec gives browsers leeway here, saying they "SHOULD use the one with
     337             :   // the smallest fitness distance", and also does not directly address the
     338             :   // problem of competing constraints at all. There is no real web interop issue
     339             :   // here since this is more about interop with other tabs on the same browser.
     340             :   //
     341             :   // We should revisit this logic once we support downscaling of resolutions and
     342             :   // decimating of frame rates, per track.
     343             : 
     344           0 :   for (auto& other : aOthers) {
     345           0 :     mWidth.TakeHighestIdeal(other->mWidth);
     346           0 :     mHeight.TakeHighestIdeal(other->mHeight);
     347             : 
     348             :     // Consider implicit 30 fps default in comparison of competing constraints.
     349             :     // Avoids 160x90x10 and 640x480 becoming 1024x768x10 (fitness distance flaw)
     350             :     // This pretty much locks in 30 fps or higher, except for single-tab use.
     351           0 :     auto frameRate = other->mFrameRate;
     352           0 :     if (frameRate.mIdeal.isNothing()) {
     353           0 :       frameRate.mIdeal.emplace(30);
     354             :     }
     355           0 :     mFrameRate.TakeHighestIdeal(frameRate);
     356             :   }
     357             : }
     358             : 
     359           0 : FlattenedConstraints::FlattenedConstraints(const NormalizedConstraints& aOther)
     360           0 : : NormalizedConstraintSet(aOther)
     361             : {
     362           0 :   for (auto& set : aOther.mAdvanced) {
     363             :     // Must only apply compatible i.e. inherently non-overconstraining sets
     364             :     // This rule is pretty much why this code is centralized here.
     365           0 :     if (mWidth.Intersects(set.mWidth) &&
     366           0 :         mHeight.Intersects(set.mHeight) &&
     367           0 :         mFrameRate.Intersects(set.mFrameRate)) {
     368           0 :       mWidth.Intersect(set.mWidth);
     369           0 :       mHeight.Intersect(set.mHeight);
     370           0 :       mFrameRate.Intersect(set.mFrameRate);
     371             :     }
     372           0 :     if (mEchoCancellation.Intersects(set.mEchoCancellation)) {
     373           0 :         mEchoCancellation.Intersect(set.mEchoCancellation);
     374             :     }
     375           0 :     if (mNoiseSuppression.Intersects(set.mNoiseSuppression)) {
     376           0 :         mNoiseSuppression.Intersect(set.mNoiseSuppression);
     377             :     }
     378           0 :     if (mAutoGainControl.Intersects(set.mAutoGainControl)) {
     379           0 :         mAutoGainControl.Intersect(set.mAutoGainControl);
     380             :     }
     381           0 :     if (mChannelCount.Intersects(set.mChannelCount)) {
     382           0 :         mChannelCount.Intersect(set.mChannelCount);
     383             :     }
     384             :   }
     385           0 : }
     386             : 
     387             : // MediaEngine helper
     388             : //
     389             : // The full algorithm for all devices. Sources that don't list capabilities
     390             : // need to fake it and hardcode some by populating mHardcodedCapabilities above.
     391             : //
     392             : // Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
     393             : 
     394             : // First, all devices have a minimum distance based on their deviceId.
     395             : // If you have no other constraints, use this one. Reused by all device types.
     396             : 
     397             : uint32_t
     398           0 : MediaConstraintsHelper::GetMinimumFitnessDistance(
     399             :     const NormalizedConstraintSet &aConstraints,
     400             :     const nsString& aDeviceId)
     401             : {
     402           0 :   return FitnessDistance(aDeviceId, aConstraints.mDeviceId);
     403             : }
     404             : 
     405             : template<class ValueType, class NormalizedRange>
     406             : /* static */ uint32_t
     407           0 : MediaConstraintsHelper::FitnessDistance(ValueType aN,
     408             :                                         const NormalizedRange& aRange)
     409             : {
     410           0 :   if (aRange.mMin > aN || aRange.mMax < aN) {
     411           0 :     return UINT32_MAX;
     412             :   }
     413           0 :   if (aN == aRange.mIdeal.valueOr(aN)) {
     414           0 :     return 0;
     415             :   }
     416           0 :   return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.value()) * 1000) /
     417           0 :                             std::max(std::abs(aN), std::abs(aRange.mIdeal.value()))));
     418             : }
     419             : 
     420             : // Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
     421             : 
     422             : /* static */ uint32_t
     423           0 : MediaConstraintsHelper::FitnessDistance(
     424             :     nsString aN,
     425             :     const NormalizedConstraintSet::StringRange& aParams)
     426             : {
     427           0 :   if (aParams.mExact.size() && aParams.mExact.find(aN) == aParams.mExact.end()) {
     428           0 :     return UINT32_MAX;
     429             :   }
     430           0 :   if (aParams.mIdeal.size() && aParams.mIdeal.find(aN) == aParams.mIdeal.end()) {
     431           0 :     return 1000;
     432             :   }
     433           0 :   return 0;
     434             : }
     435             : 
     436             : template<class MediaEngineSourceType>
     437             : const char*
     438           0 : MediaConstraintsHelper::FindBadConstraint(
     439             :     const NormalizedConstraints& aConstraints,
     440             :     const MediaEngineSourceType& aMediaEngineSource,
     441             :     const nsString& aDeviceId)
     442             : {
     443             :   class MockDevice
     444             :   {
     445             :   public:
     446           0 :     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockDevice);
     447             : 
     448           0 :     explicit MockDevice(const MediaEngineSourceType* aMediaEngineSource,
     449             :                         const nsString& aDeviceId)
     450             :     : mMediaEngineSource(aMediaEngineSource),
     451             :       // The following dud code exists to avoid 'unused typedef' error on linux.
     452           0 :       mDeviceId(MockDevice::HasThreadSafeRefCnt::value ? aDeviceId : nsString()) {}
     453             : 
     454           0 :     uint32_t GetBestFitnessDistance(
     455             :         const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
     456             :         bool aIsChrome)
     457             :     {
     458           0 :       return mMediaEngineSource->GetBestFitnessDistance(aConstraintSets,
     459           0 :                                                         mDeviceId);
     460             :     }
     461             : 
     462             :   private:
     463           0 :     ~MockDevice() {}
     464             : 
     465             :     const MediaEngineSourceType* mMediaEngineSource;
     466             :     nsString mDeviceId;
     467             :   };
     468             : 
     469           0 :   Unused << typename MockDevice::HasThreadSafeRefCnt();
     470             : 
     471           0 :   nsTArray<RefPtr<MockDevice>> devices;
     472           0 :   devices.AppendElement(new MockDevice(&aMediaEngineSource, aDeviceId));
     473           0 :   return FindBadConstraint(aConstraints, devices);
     474             : }
     475             : 
     476             : void
     477           0 : MediaConstraintsHelper::ConvertOldWithWarning(
     478             :     const dom::OwningBooleanOrConstrainBooleanParameters& old,
     479             :     dom::OwningBooleanOrConstrainBooleanParameters& to,
     480             :     const char* aMessageName,
     481             :     nsPIDOMWindowInner* aWindow) {
     482           0 :   if ((old.IsBoolean() ||
     483           0 :        old.GetAsConstrainBooleanParameters().mExact.WasPassed() ||
     484           0 :        old.GetAsConstrainBooleanParameters().mIdeal.WasPassed()) &&
     485           0 :       !(to.IsBoolean() ||
     486           0 :         to.GetAsConstrainBooleanParameters().mExact.WasPassed() ||
     487           0 :         to.GetAsConstrainBooleanParameters().mIdeal.WasPassed())) {
     488           0 :     nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
     489           0 :     if (doc) {
     490           0 :       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     491           0 :                                       NS_LITERAL_CSTRING("DOM"), doc,
     492             :                                       nsContentUtils::eDOM_PROPERTIES,
     493           0 :                                       aMessageName);
     494             :     }
     495           0 :     if (old.IsBoolean()) {
     496           0 :       to.SetAsBoolean() = old.GetAsBoolean();
     497             :     } else {
     498           0 :       to.SetAsConstrainBooleanParameters() = old.GetAsConstrainBooleanParameters();
     499             :     }
     500             :   }
     501           0 : }
     502             : 
     503             : }

Generated by: LCOV version 1.13