Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  MediaTrackConstraints.cpp

  Sprache: C
 

/* 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,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "MediaTrackConstraints.h"

#include <algorithm>
#include <iterator>
#include <limits>

#include "mozilla/MediaManager.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"

#ifdef MOZ_WEBRTC
namespace mozilla {
extern LazyLogModule gMediaManagerLog;
}
#else
static mozilla::LazyLogModule gMediaManagerLog("MediaManager");
#endif
#define LOG(...) MOZ_LOG_FMT(gMediaManagerLog, LogLevel::Debug, __VA_ARGS__)

namespace mozilla {

using dom::CallerType;
using dom::VideoResizeModeEnum;

template <class ValueType>
template <class ConstrainRange>
void NormalizedConstraintSet::Range<ValueType>::SetFrom(
    const ConstrainRange& aOther) {
  if (aOther.mIdeal.WasPassed()) {
    mIdeal.emplace(aOther.mIdeal.Value());
  }
  if (aOther.mExact.WasPassed()) {
    mMin = aOther.mExact.Value();
    mMax = aOther.mExact.Value();
  } else {
    if (aOther.mMin.WasPassed()) {
      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.Get(false));
      counter = aOther.Get(false);
      denominator = 1;
    } else {
      if (!denominator) {
        counter = Get(false);
        denominator = 1;
      }
      counter += aOther.Get(false);
      denominator++;
    }
  }
  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 nsCString& aName,
    const dom::Optional<dom::OwningLongOrConstrainLongRange>& aOther,
    bool advanced)
    : Range<int32_t>(aName,
                     1 + INT32_MIN,  // +1 avoids Windows compiler bug
                     INT32_MAX) {
  if (!aOther.WasPassed()) {
    return;
  }
  const auto& other = aOther.Value();
  if (other.IsLong()) {
    if (advanced) {
      mMin = mMax = other.GetAsLong();
    } else {
      mIdeal.emplace(other.GetAsLong());
    }
  } else {
    SetFrom(other.GetAsConstrainLongRange());
  }
}

NormalizedConstraintSet::LongLongRange::LongLongRange(
    const nsCString& aName, const dom::Optional<int64_t>& aOther)
    : Range<int64_t>(aName,
                     1 + INT64_MIN,  // +1 avoids Windows compiler bug
                     INT64_MAX) {
  if (aOther.WasPassed()) {
    mIdeal.emplace(aOther.Value());
  }
}

NormalizedConstraintSet::DoubleRange::DoubleRange(
    const nsCString& aName,
    const dom::Optional<dom::OwningDoubleOrConstrainDoubleRange>& aOther,
    bool advanced)
    : Range<double>(aName, -std::numeric_limits<double>::infinity(),
                    std::numeric_limits<double>::infinity()) {
  if (!aOther.WasPassed()) {
    return;
  }
  const auto& other = aOther.Value();
  if (other.IsDouble()) {
    if (advanced) {
      mMin = mMax = other.GetAsDouble();
    } else {
      mIdeal.emplace(other.GetAsDouble());
    }
  } else {
    SetFrom(other.GetAsConstrainDoubleRange());
  }
}

NormalizedConstraintSet::BooleanRange::BooleanRange(
    const nsCString& aName,
    const dom::Optional<dom::OwningBooleanOrConstrainBooleanParameters>& aOther,
    bool advanced)
    : Range<bool>(aName, false, true) {
  if (!aOther.WasPassed()) {
    return;
  }
  const auto& other = aOther.Value();
  if (other.IsBoolean()) {
    if (advanced) {
      mMin = mMax = other.GetAsBoolean();
    } else {
      mIdeal.emplace(other.GetAsBoolean());
    }
  } else {
    const auto& r = other.GetAsConstrainBooleanParameters();
    if (r.mIdeal.WasPassed()) {
      mIdeal.emplace(r.mIdeal.Value());
    }
    if (r.mExact.WasPassed()) {
      mMin = r.mExact.Value();
      mMax = r.mExact.Value();
    }
  }
}

NormalizedConstraintSet::StringRange::StringRange(
    const nsCString& aName,
    const dom::Optional<
        dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters>&
        aOther,
    bool advanced)
    : BaseRange(aName) {
  if (!aOther.WasPassed()) {
    return;
  }
  const auto& other = aOther.Value();
  if (other.IsString()) {
    if (advanced) {
      mExact.insert(other.GetAsString());
    } else {
      mIdeal.insert(other.GetAsString());
    }
  } else if (other.IsStringSequence()) {
    if (advanced) {
      mExact.clear();
      for (const auto& str : other.GetAsStringSequence()) {
        mExact.insert(str);
      }
    } else {
      mIdeal.clear();
      for (const auto& str : other.GetAsStringSequence()) {
        mIdeal.insert(str);
      }
    }
  } else {
    SetFrom(other.GetAsConstrainDOMStringParameters());
  }
}

void NormalizedConstraintSet::StringRange::SetFrom(
    const dom::ConstrainDOMStringParameters& aOther) {
  if (aOther.mIdeal.WasPassed()) {
    mIdeal.clear();
    if (aOther.mIdeal.Value().IsString()) {
      mIdeal.insert(aOther.mIdeal.Value().GetAsString());
    } else {
      for (const auto& str : aOther.mIdeal.Value().GetAsStringSequence()) {
        mIdeal.insert(str);
      }
    }
  }
  if (aOther.mExact.WasPassed()) {
    mExact.clear();
    if (aOther.mExact.Value().IsString()) {
      mExact.insert(aOther.mExact.Value().GetAsString());
    } else {
      for (const auto& str : aOther.mExact.Value().GetAsStringSequence()) {
        mExact.insert(str);
      }
    }
  }
}

auto NormalizedConstraintSet::StringRange::Clamp(const ValueType& n) const
    -> ValueType {
  if (mExact.empty()) {
    return n;
  }
  ValueType result;
  for (const auto& entry : n) {
    if (mExact.find(entry) != mExact.end()) {
      result.insert(entry);
    }
  }
  return result;
}

bool NormalizedConstraintSet::StringRange::Intersects(
    const StringRange& aOther) const {
  if (mExact.empty() || aOther.mExact.empty()) {
    return true;
  }

  ValueType intersection;
  set_intersection(mExact.begin(), mExact.end(), aOther.mExact.begin(),
                   aOther.mExact.end(),
                   std::inserter(intersection, intersection.begin()));
  return !intersection.empty();
}

void NormalizedConstraintSet::StringRange::Intersect(
    const StringRange& aOther) {
  if (aOther.mExact.empty()) {
    return;
  }

  ValueType intersection;
  set_intersection(mExact.begin(), mExact.end(), aOther.mExact.begin(),
                   aOther.mExact.end(),
                   std::inserter(intersection, intersection.begin()));
  mExact = std::move(intersection);
}

bool NormalizedConstraintSet::StringRange::Merge(const StringRange& aOther) {
  if (!Intersects(aOther)) {
    return false;
  }
  Intersect(aOther);

  ValueType unioned;
  set_union(mIdeal.begin(), mIdeal.end(), aOther.mIdeal.begin(),
            aOther.mIdeal.end(), std::inserter(unioned, unioned.begin()));
  mIdeal = std::move(unioned);
  return true;
}

NormalizedConstraints::NormalizedConstraints(
    const dom::MediaTrackConstraints& aOther)
    : NormalizedConstraintSet(aOther, false) {
  if (aOther.mAdvanced.WasPassed()) {
    for (const auto& entry : aOther.mAdvanced.Value()) {
      mAdvanced.push_back(NormalizedConstraintSet(entry, true));
    }
  }
}

FlattenedConstraints::FlattenedConstraints(const NormalizedConstraints& aOther)
    : NormalizedConstraintSet(aOther) {
  for (const 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) &&
        mFrameRate.Intersects(set.mFrameRate)) {
      mWidth.Intersect(set.mWidth);
      mHeight.Intersect(set.mHeight);
      mFrameRate.Intersect(set.mFrameRate);
    }
    if (mEchoCancellation.Intersects(set.mEchoCancellation)) {
      mEchoCancellation.Intersect(set.mEchoCancellation);
    }
    if (mNoiseSuppression.Intersects(set.mNoiseSuppression)) {
      mNoiseSuppression.Intersect(set.mNoiseSuppression);
    }
    if (mAutoGainControl.Intersects(set.mAutoGainControl)) {
      mAutoGainControl.Intersect(set.mAutoGainControl);
    }
    if (mChannelCount.Intersects(set.mChannelCount)) {
      mChannelCount.Intersect(set.mChannelCount);
    }
  }
}

// MediaEngine helper
//
// The full algorithm for all devices.
//
// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX

// First, all devices have a minimum distance based on their deviceId.
// If you have no other constraints, use this one. Reused by all device types.

/* static */
bool MediaConstraintsHelper::SomeSettingsFit(
    const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    const nsTArray<RefPtr<LocalMediaDevice>>& aDevices) {
  nsTArray<const NormalizedConstraintSet*> sets;
  sets.AppendElement(&aConstraints);

  MOZ_ASSERT(!aDevices.IsEmpty());
  for (const auto& device : aDevices) {
    auto distance =
        device->GetBestFitnessDistance(sets, aPrefs, CallerType::NonSystem);
    if (distance != UINT32_MAX) {
      return true;
    }
  }
  return false;
}

// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX

/* static */
uint32_t MediaConstraintsHelper::FitnessDistance(
    const Maybe<nsString>& aN,
    const NormalizedConstraintSet::StringRange& aParams) {
  if (!aParams.mExact.empty() &&
      (aN.isNothing() || aParams.mExact.find(*aN) == aParams.mExact.end())) {
    return UINT32_MAX;
  }
  if (!aParams.mIdeal.empty() &&
      (aN.isNothing() || aParams.mIdeal.find(*aN) == aParams.mIdeal.end())) {
    return 1000;
  }
  return 0;
}

/* static */ const char* MediaConstraintsHelper::SelectSettings(
    const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    nsTArray<RefPtr<LocalMediaDevice>>& aDevices, CallerType aCallerType) {
  const auto& c = aConstraints;
  LogConstraints(c);

  if (!aDevices.IsEmpty() &&
      aDevices[0]->Kind() == dom::MediaDeviceKind::Videoinput &&
      aPrefs.mResizeModeEnabled) {
    // Check invalid exact resizeMode constraint (not a device property)
    nsString none =
        NS_ConvertASCIItoUTF16(dom::GetEnumString(VideoResizeModeEnum::None));
    nsString crop = NS_ConvertASCIItoUTF16(
        dom::GetEnumString(VideoResizeModeEnum::Crop_and_scale));
    if (FitnessDistance(Some(none), c.mResizeMode) == UINT32_MAX &&
        FitnessDistance(Some(crop), c.mResizeMode) == UINT32_MAX) {
      return "resizeMode";
    }
  }

  // First apply top-level constraints.

  // Stack constraintSets that pass, starting with the required one, because the
  // whole stack must be re-satisfied each time a capability-set is ruled out
  // (this avoids storing state or pushing algorithm into the lower-level code).
  nsTArray<RefPtr<LocalMediaDevice>> unsatisfactory;
  nsTArray<const NormalizedConstraintSet*> aggregateConstraints;
  aggregateConstraints.AppendElement(&c);

  std::multimap<uint32_t, RefPtr<LocalMediaDevice>> ordered;

  for (uint32_t i = 0; i < aDevices.Length();) {
    uint32_t distance = aDevices[i]->GetBestFitnessDistance(
        aggregateConstraints, aPrefs, aCallerType);
    if (distance == UINT32_MAX) {
      unsatisfactory.AppendElement(std::move(aDevices[i]));
      aDevices.RemoveElementAt(i);
    } else {
      ordered.insert(std::make_pair(distance, aDevices[i]));
      ++i;
    }
  }
  if (aDevices.IsEmpty()) {
    return FindBadConstraint(c, aPrefs, unsatisfactory);
  }

  // Order devices by shortest distance
  for (auto& ordinal : ordered) {
    aDevices.RemoveElement(ordinal.second);
    aDevices.AppendElement(ordinal.second);
  }

  // Then apply advanced constraints.

  for (const auto& advanced : c.mAdvanced) {
    aggregateConstraints.AppendElement(&advanced);
    nsTArray<RefPtr<LocalMediaDevice>> rejects;
    for (uint32_t j = 0; j < aDevices.Length();) {
      uint32_t distance = aDevices[j]->GetBestFitnessDistance(
          aggregateConstraints, aPrefs, aCallerType);
      if (distance == UINT32_MAX) {
        rejects.AppendElement(std::move(aDevices[j]));
        aDevices.RemoveElementAt(j);
      } else {
        ++j;
      }
    }
    if (aDevices.IsEmpty()) {
      aDevices.AppendElements(std::move(rejects));
      aggregateConstraints.RemoveLastElement();
    }
  }
  return nullptr;
}

/* static */ const char* MediaConstraintsHelper::FindBadConstraint(
    const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    const nsTArray<RefPtr<LocalMediaDevice>>& aDevices) {
  // The spec says to report a constraint that satisfies NONE
  // of the sources. Unfortunately, this is a bit laborious to find out, and
  // requires updating as new constraints are added!
  const auto& c = aConstraints;

  if (aDevices.IsEmpty() ||
      !SomeSettingsFit(NormalizedConstraints(), aPrefs, aDevices)) {
    return "";
  }
  {
    NormalizedConstraints fresh;
    fresh.mDeviceId = c.mDeviceId;
    if (!SomeSettingsFit(fresh, aPrefs, aDevices)) {
      return "deviceId";
    }
  }
  {
    NormalizedConstraints fresh;
    fresh.mGroupId = c.mGroupId;
    if (!SomeSettingsFit(fresh, aPrefs, aDevices)) {
      return "groupId";
    }
  }
  {
    NormalizedConstraints fresh;
    fresh.mWidth = c.mWidth;
    if (!SomeSettingsFit(fresh, aPrefs, aDevices)) {
      return "width";
    }
  }
  {
    NormalizedConstraints fresh;
    fresh.mHeight = c.mHeight;
    if (!SomeSettingsFit(fresh, aPrefs, aDevices)) {
      return "height";
    }
  }
  {
    NormalizedConstraints fresh;
    fresh.mFrameRate = c.mFrameRate;
    if (!SomeSettingsFit(fresh, aPrefs, aDevices)) {
      return "frameRate";
    }
  }
  {
    NormalizedConstraints fresh;
    fresh.mFacingMode = c.mFacingMode;
    if (!SomeSettingsFit(fresh, aPrefs, aDevices)) {
      return "facingMode";
    }
  }
  return "";
}

/* static */
const char* MediaConstraintsHelper::FindBadConstraint(
    const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    const MediaDevice* aMediaDevice) {
  NormalizedConstraints c(aConstraints);
  NormalizedConstraints empty;
  c.mDeviceId = empty.mDeviceId;
  c.mGroupId = empty.mGroupId;
  AutoTArray<RefPtr<LocalMediaDevice>, 1> devices;
  devices.EmplaceBack(
      new LocalMediaDevice(aMediaDevice, u""_ns, u""_ns, u""_ns));
  return FindBadConstraint(c, aPrefs, devices);
}

static void LogConstraintStringRange(
    const NormalizedConstraintSet::StringRange& aRange) {
  if (aRange.mExact.size() <= 1 && aRange.mIdeal.size() <= 1) {
    LOG("  {}: {{ exact: [{}], ideal: [{}] }}", aRange.mName.get(),
        (aRange.mExact.empty()
             ? ""
             : NS_ConvertUTF16toUTF8(*aRange.mExact.begin()).get()),
        (aRange.mIdeal.empty()
             ? ""
             : NS_ConvertUTF16toUTF8(*aRange.mIdeal.begin()).get()));
  } else {
    LOG("  {}: {{ exact: [", aRange.mName.get());
    for (const auto& entry : aRange.mExact) {
      LOG("      {},", NS_ConvertUTF16toUTF8(entry).get());
    }
    LOG("    ], ideal: [");
    for (const auto& entry : aRange.mIdeal) {
      LOG("      {},", NS_ConvertUTF16toUTF8(entry).get());
    }
    LOG("    ]}}");
  }
}

template <typename T>
static void LogConstraintRange(
    const NormalizedConstraintSet::Range<T>& aRange) {
  if (aRange.mIdeal.isSome()) {
    LOG("  {}: {{ min: {}, max: {}, ideal: {} }}", aRange.mName.get(),
        aRange.mMin, aRange.mMax, aRange.mIdeal.valueOr(0));
  } else {
    LOG("  {}: {{ min: {}, max: {} }}", aRange.mName.get(), aRange.mMin,
        aRange.mMax);
  }
}

template <>
void LogConstraintRange(const NormalizedConstraintSet::Range<double>& aRange) {
  if (aRange.mIdeal.isSome()) {
    LOG("  {}: {{ min: {}, max: {}, ideal: {} }}", aRange.mName.get(),
        aRange.mMin, aRange.mMax, aRange.mIdeal.valueOr(0));
  } else {
    LOG("  {}: {{ min: {}, max: {} }}", aRange.mName.get(), aRange.mMin,
        aRange.mMax);
  }
}

/* static */
void MediaConstraintsHelper::LogConstraints(
    const NormalizedConstraintSet& aConstraints) {
  const auto& c = aConstraints;
  LOG("Constraints: {{");
  LOG("{}", [&]() {
    LogConstraintRange(c.mWidth);
    LogConstraintRange(c.mHeight);
    LogConstraintRange(c.mFrameRate);
    LogConstraintStringRange(c.mMediaSource);
    LogConstraintStringRange(c.mFacingMode);
    LogConstraintStringRange(c.mResizeMode);
    LogConstraintStringRange(c.mDeviceId);
    LogConstraintStringRange(c.mGroupId);
    LogConstraintRange(c.mEchoCancellation);
    LogConstraintRange(c.mAutoGainControl);
    LogConstraintRange(c.mNoiseSuppression);
    LogConstraintRange(c.mChannelCount);
    return "}";
  }());
}

/* static */
Maybe<VideoResizeModeEnum> MediaConstraintsHelper::GetResizeMode(
    const NormalizedConstraintSet& aConstraints,
    const MediaEnginePrefs& aPrefs) {
  if (!aPrefs.mResizeModeEnabled) {
    return Nothing();
  }
  auto defaultResizeMode = aPrefs.mResizeMode;
  nsString defaultResizeModeString =
      NS_ConvertASCIItoUTF16(dom::GetEnumString(defaultResizeMode));
  uint32_t distanceToDefault = MediaConstraintsHelper::FitnessDistance(
      Some(defaultResizeModeString), aConstraints.mResizeMode);
  if (distanceToDefault == 0) {
    return Some(defaultResizeMode);
  }
  VideoResizeModeEnum otherResizeMode =
      (defaultResizeMode == VideoResizeModeEnum::None)
          ? VideoResizeModeEnum::Crop_and_scale
          : VideoResizeModeEnum::None;
  nsString otherResizeModeString =
      NS_ConvertASCIItoUTF16(dom::GetEnumString(otherResizeMode));
  uint32_t distanceToOther = MediaConstraintsHelper::FitnessDistance(
      Some(otherResizeModeString), aConstraints.mResizeMode);
  return Some((distanceToDefault <= distanceToOther) ? defaultResizeMode
                                                     : otherResizeMode);
}

}  // namespace mozilla

#undef LOG

Messung V0.5 in Prozent
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002