Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  MediaEngineRemoteVideoSource.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 "MediaEngineRemoteVideoSource.h"

#include "CamerasChild.h"
#include "ImageContainer.h"
#include "MediaManager.h"
#include "MediaTrackConstraints.h"
#include "PerformanceRecorder.h"
#include "VideoSegment.h"
#include "common_video/include/video_frame_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/MediaTrackCapabilitiesBinding.h"
#include "mozilla/dom/MediaTrackSettingsBinding.h"
#include "mozilla/gfx/Point.h"

namespace mozilla {

extern LazyLogModule gMediaManagerLog;
#define LOG(...) MOZ_LOG_FMT(gMediaManagerLog, LogLevel::Debug, __VA_ARGS__)
#define LOG_FRAME(...) \
  MOZ_LOG_FMT(gMediaManagerLog, LogLevel::Verbose, __VA_ARGS__)

using dom::MediaSourceEnum;
using dom::MediaTrackCapabilities;
using dom::MediaTrackConstraints;
using dom::MediaTrackConstraintSet;
using dom::MediaTrackSettings;
using dom::VideoFacingModeEnum;
using dom::VideoResizeModeEnum;

/* static */
camera::CaptureEngine MediaEngineRemoteVideoSource::CaptureEngine(
    MediaSourceEnum aMediaSource) {
  switch (aMediaSource) {
    case MediaSourceEnum::Browser:
      return camera::BrowserEngine;
    case MediaSourceEnum::Camera:
      return camera::CameraEngine;
    case MediaSourceEnum::Screen:
      return camera::ScreenEngine;
    case MediaSourceEnum::Window:
      return camera::WinEngine;
    default:
      MOZ_CRASH();
  }
}

static Maybe<VideoFacingModeEnum> GetFacingMode(const nsString& aDeviceName) {
  // Set facing mode based on device name.
#if defined(ANDROID)
  // Names are generated. Example: "Camera 0, Facing back, Orientation 90"
  //
  // See media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/
  // webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java

  if (aDeviceName.Find(u"Facing back"_ns) != kNotFound) {
    return Some(VideoFacingModeEnum::Environment);
  }
  if (aDeviceName.Find(u"Facing front"_ns) != kNotFound) {
    return Some(VideoFacingModeEnum::User);
  }
#endif  // ANDROID
#ifdef XP_WIN
  // The cameras' name of Surface book are "Microsoft Camera Front" and
  // "Microsoft Camera Rear" respectively.

  if (aDeviceName.Find(u"Front"_ns) != kNotFound) {
    return Some(VideoFacingModeEnum::User);
  }
  if (aDeviceName.Find(u"Rear"_ns) != kNotFound) {
    return Some(VideoFacingModeEnum::Environment);
  }
#endif  // WINDOWS

  return Nothing();
}

struct DesiredSizeInput {
  NormalizedConstraints mConstraints;
  Maybe<bool> mCanCropAndScale;
  Maybe<int32_t> mCapabilityWidth;
  Maybe<int32_t> mCapabilityHeight;
  camera::CaptureEngine mCapEngine;
  int32_t mInputWidth;
  int32_t mInputHeight;
  int32_t mRotation;
};

static gfx::IntSize CalculateDesiredSize(DesiredSizeInput aInput) {
  if (!aInput.mCanCropAndScale.valueOr(aInput.mCapEngine !=
                                       camera::CameraEngine)) {
    // Don't scale to constraints in resizeMode "none".
    // If resizeMode is disabled, follow our legacy behavior of downscaling for
    // screen capture but not for cameras.
    aInput.mConstraints.mWidth.mIdeal = Nothing();
    aInput.mConstraints.mHeight.mIdeal = Nothing();
  }

  if (aInput.mRotation == 90 || aInput.mRotation == 270) {
    // This frame is rotated, so what was negotiated as width is now height,
    // and vice versa.
    std::swap(aInput.mConstraints.mWidth, aInput.mConstraints.mHeight);
    std::swap(aInput.mCapabilityWidth, aInput.mCapabilityWidth);
  }

  // Account for a shared camera giving us higher resolution than we asked for.
  const int32_t inputWidth =
      std::max(2, aInput.mCapabilityWidth.valueOr(aInput.mInputWidth));
  const int32_t inputHeight =
      std::max(2, aInput.mCapabilityHeight.valueOr(aInput.mInputHeight));

  // This logic works for both camera and screen sharing case.
  // In VideoResizeModeEnum::None, ideal dimensions are absent.
  // In screen sharing, min and exact dimensions are forbidden.
  int32_t dst_width = aInput.mConstraints.mWidth.Get(inputWidth);
  int32_t dst_height = aInput.mConstraints.mHeight.Get(inputHeight);

  // We must not upscale.
  dst_width = std::min(dst_width, inputWidth);
  dst_height = std::min(dst_height, inputHeight);

  if (aInput.mCapEngine != camera::CameraEngine ||
      !aInput.mConstraints.mWidth.mIdeal ||
      !aInput.mConstraints.mHeight.mIdeal) {
    // Scale down without cropping.
    // Cropping is not allowed by spec for desktop capture.
    // For cameras, it only makes sense when not both ideal width and
    // height are given, assuming they're within min/max constraints.

    // Max constraints decide the envelope.
    const double scale_width_strict =
        std::min(1.0, AssertedCast<double>(aInput.mConstraints.mWidth.mMax) /
                          AssertedCast<double>(inputWidth));
    const double scale_height_strict =
        std::min(1.0, AssertedCast<double>(aInput.mConstraints.mHeight.mMax) /
                          AssertedCast<double>(inputHeight));

    double scale_width =
        AssertedCast<double>(dst_width) / AssertedCast<double>(inputWidth);
    double scale_height =
        AssertedCast<double>(dst_height) / AssertedCast<double>(inputHeight);

    // If both ideal width & ideal height are absent, scale is 1, but
    // if one is present and the other not, scale precisely to the one present.
    // If both are present, scale to the smaller one.
    // This works because the fitness distance for width and height is shortest
    // where either dimension exactly matches its ideal constraint.
    // Also adapt to max constraints.
    double scale = std::min(
        {scale_width, scale_height, scale_width_strict, scale_height_strict});

    dst_width = SaturatingCast<int32_t>(
        std::round(scale * AssertedCast<double>(inputWidth)));
    dst_height = SaturatingCast<int32_t>(
        std::round(scale * AssertedCast<double>(inputHeight)));
  }

  if (aInput.mCapEngine == camera::CameraEngine) {
    // For cameras we are allowed to crop. Adapt to min constraints.
    dst_width = aInput.mConstraints.mWidth.Clamp(dst_width);
    dst_height = aInput.mConstraints.mHeight.Clamp(dst_height);
  }

  // Ensure width and height are at least two. Smaller frames can lead to
  // problems with scaling and video encoding.
  dst_width = std::max(2, dst_width);
  dst_height = std::max(2, dst_height);

  return {dst_width, dst_height};
}

MediaEngineRemoteVideoSource::MediaEngineRemoteVideoSource(
    const MediaDevice* aMediaDevice)
    : mCapEngine(CaptureEngine(aMediaDevice->mMediaSource)),
      mTrackingId(CaptureEngineToTrackingSourceStr(mCapEngine), 0),
      mMutex("MediaEngineRemoteVideoSource::mMutex"),
      mRescalingBufferPool(/* zero_initialize */ false,
                           /* max_number_of_buffers */ 1),
      mSettingsUpdatedByFrame(MakeAndAddRef<media::Refcountable<AtomicBool>>()),
      mSettings(MakeAndAddRef<media::Refcountable<MediaTrackSettings>>()),
      mTrackCapabilities(
          MakeAndAddRef<media::Refcountable<MediaTrackCapabilities>>()),
      mFirstFramePromise(mFirstFramePromiseHolder.Ensure(__func__)),
      mCalculation(kFitness),
      mPrefs(MakeUnique<MediaEnginePrefs>()),
      mMediaDevice(aMediaDevice),
      mDeviceUUID(NS_ConvertUTF16toUTF8(aMediaDevice->mRawID)) {
  LOG("{}", __PRETTY_FUNCTION__);
  if (mCapEngine == camera::CameraEngine) {
    // Only cameras can have a facing mode.
    Maybe<VideoFacingModeEnum> facingMode =
        GetFacingMode(mMediaDevice->mRawName);
    if (facingMode.isSome()) {
      mFacingMode.emplace(
          NS_ConvertASCIItoUTF16(dom::GetEnumString(*facingMode)));
    }
  }
}

/*static*/
already_AddRefed<MediaEngineRemoteVideoSource>
MediaEngineRemoteVideoSource::CreateFrom(
    const MediaEngineRemoteVideoSource* aSource,
    const MediaDevice* aMediaDevice) {
  auto src = MakeRefPtr<MediaEngineRemoteVideoSource>(aMediaDevice);
  *static_cast<MediaTrackSettings*>(src->mSettings) = *aSource->mSettings;
  *static_cast<MediaTrackCapabilities*>(src->mTrackCapabilities) =
      *aSource->mTrackCapabilities;
  {
    MutexAutoLock lock(aSource->mMutex);
    src->mIncomingImageSize = aSource->mIncomingImageSize;
  }
  return src.forget();
}

MediaEngineRemoteVideoSource::~MediaEngineRemoteVideoSource() {
  if (mCaptureId >= 0) {
    camera::CamerasChild::RemoveCallbackIfExists(mCaptureId);
  }
  mFirstFramePromiseHolder.RejectIfExists(NS_ERROR_ABORT, __func__);
}

static inline DistanceCalculation ToDistanceCalculation(
    VideoResizeModeEnum aMode) {
  switch (aMode) {
    case VideoResizeModeEnum::None:
      return kFitness;
    case VideoResizeModeEnum::Crop_and_scale:
      return kFeasibility;
  }
  MOZ_CRASH("Unexpected resize mode");
}

static inline const char* ToString(DistanceCalculation aMode) {
  switch (aMode) {
    case kFitness:
      return "kFitness";
    case kFeasibility:
      return "kFeasibility";
  }
  MOZ_CRASH("Unexpected distance calculation");
}

nsresult MediaEngineRemoteVideoSource::Allocate(
    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    uint64_t aWindowID, const char** aOutBadConstraint) {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  MOZ_ASSERT(mState == kReleased);

  NormalizedConstraints c(aConstraints);
  const auto resizeMode = MediaConstraintsHelper::GetResizeMode(c, aPrefs);
  const auto legacyDistanceMode =
      mCapEngine == camera::CameraEngine ? kFitness : kFeasibility;
  const auto distanceMode =
      resizeMode.map(&ToDistanceCalculation).valueOr(legacyDistanceMode);
  webrtc::CaptureCapability newCapability;
  LOG("ChooseCapability({}) for mCapability (Allocate) ++",
      ToString(distanceMode));
  if (!ChooseCapability(c, aPrefs, newCapability, distanceMode,
                        aOutBadConstraint)) {
    if (aOutBadConstraint && !*aOutBadConstraint) {
      *aOutBadConstraint =
          MediaConstraintsHelper::FindBadConstraint(c, aPrefs, mMediaDevice);
    }
    return NS_ERROR_FAILURE;
  }
  LOG("ChooseCapability({}) for mCapability (Allocate) --",
      ToString(distanceMode));

  mCaptureId =
      camera::GetChildAndCall(&camera::CamerasChild::AllocateCapture,
                              mCapEngine, mDeviceUUID.get(), aWindowID);
  if (mCaptureId < 0) {
    return NS_ERROR_FAILURE;
  }

  DesiredSizeInput input{};
  double framerate = 0.0;
  {
    MutexAutoLock lock(mMutex);
    mState = kAllocated;
    mCapability = std::move(newCapability);
    mCalculation = distanceMode;
    mConstraints = Some(c);
    *mPrefs = aPrefs;
    mTrackingId =
        TrackingId(CaptureEngineToTrackingSourceStr(mCapEngine), mCaptureId);
    const int32_t& cw = mCapability.width;
    const int32_t& ch = mCapability.height;
    const double maxFPS = AssertedCast<double>(mCapability.maxFPS);
    input = {
        .mConstraints = c,
        .mCanCropAndScale = resizeMode.map([](auto aRM) {
          return aRM == dom::VideoResizeModeEnum::Crop_and_scale;
        }),
        .mCapabilityWidth = cw ? Some(cw) : Nothing(),
        .mCapabilityHeight = ch ? Some(ch) : Nothing(),
        .mCapEngine = mCapEngine,
        .mInputWidth = cw ? cw : mIncomingImageSize.width,
        .mInputHeight = ch ? ch : mIncomingImageSize.height,
        .mRotation = 0,
    };
    framerate = input.mCanCropAndScale.valueOr(false)
                    ? std::min(mConstraints->mFrameRate.Get(maxFPS), maxFPS)
                    : maxFPS;
  }

  auto dstSize = CalculateDesiredSize(input);

  NS_DispatchToMainThread(NS_NewRunnableFunction(
      "MediaEngineRemoteVideoSource::Allocate::MainUpdate",
      [settings = mSettings, caps = mTrackCapabilities, dstSize, framerate,
       facingMode = mFacingMode, resizeMode]() {
        *settings = dom::MediaTrackSettings();

        settings->mWidth.Construct(dstSize.width);
        settings->mHeight.Construct(dstSize.height);
        settings->mFrameRate.Construct(framerate);

        caps->mFacingMode.Reset();
        if (facingMode) {
          settings->mFacingMode.Construct(*facingMode);
          caps->mFacingMode.Construct(nsTArray{*facingMode});
        }

        caps->mResizeMode.Reset();
        if (resizeMode) {
          nsString noneString, cropString;
          noneString.AssignASCII(dom::GetEnumString(VideoResizeModeEnum::None));
          cropString.AssignASCII(
              dom::GetEnumString(VideoResizeModeEnum::Crop_and_scale));
          settings->mResizeMode.Construct(
              *resizeMode == VideoResizeModeEnum::Crop_and_scale ? cropString
                                                                 : noneString);
          caps->mResizeMode.Construct(nsTArray{noneString, cropString});
        }
      }));

  LOG("Video device {} allocated", mCaptureId);
  return NS_OK;
}

nsresult MediaEngineRemoteVideoSource::Deallocate() {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  MOZ_ASSERT(mState == kStopped || mState == kAllocated);

  if (mTrack) {
    mTrack->End();
  }

  {
    MutexAutoLock lock(mMutex);

    mTrack = nullptr;
    mPrincipal = PRINCIPAL_HANDLE_NONE;
    mState = kReleased;
  }

  // Stop() has stopped capture synchronously on the media thread before we get
  // here, so there are no longer any callbacks on an IPC thread accessing
  // mImageContainer or mRescalingBufferPool.
  mImageContainer = nullptr;
  mRescalingBufferPool.Release();

  LOG("Video device {} deallocated", mCaptureId);

  int error = camera::GetChildAndCall(&camera::CamerasChild::ReleaseCapture,
                                      mCapEngine, mCaptureId);

  if (error == camera::kSuccess) {
    return NS_OK;
  }

  if (error == camera::kIpcError) {
    // Failure can occur when the parent process is shutting down, and the IPC
    // channel is down. We still consider the capturer deallocated in this
    // case, since it cannot deliver frames without the IPC channel open.
    return NS_OK;
  }

  MOZ_ASSERT(error == camera::kError);
  return NS_ERROR_FAILURE;
}

void MediaEngineRemoteVideoSource::SetTrack(const RefPtr<MediaTrack>& aTrack,
                                            const PrincipalHandle& aPrincipal) {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  MOZ_ASSERT(mState == kAllocated);
  MOZ_ASSERT(!mTrack);
  MOZ_ASSERT(aTrack);
  MOZ_ASSERT(aTrack->AsSourceTrack());

  if (!mImageContainer) {
    mImageContainer = MakeAndAddRef<layers::ImageContainer>(
        layers::ImageUsageType::Webrtc, layers::ImageContainer::ASYNCHRONOUS);
  }

  {
    MutexAutoLock lock(mMutex);
    mTrack = aTrack->AsSourceTrack();
    mPrincipal = aPrincipal;
  }
}

nsresult MediaEngineRemoteVideoSource::Start() {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  MOZ_ASSERT(mState == kAllocated || mState == kStarted || mState == kStopped);
  MOZ_ASSERT(mTrack);

  NormalizedConstraints constraints;
  {
    MutexAutoLock lock(mMutex);
    mState = kStarted;
    constraints = *mConstraints;
  }

  // Tell CamerasParent what resizeMode was selected, as it doesn't have access
  // to MediaEnginePrefs.
  const auto resizeMode = mCalculation == kFeasibility
                              ? VideoResizeModeEnum::Crop_and_scale
                              : VideoResizeModeEnum::None;
  const auto resizeModeString =
      NS_ConvertASCIItoUTF16(dom::GetEnumString(resizeMode));
  constraints.mResizeMode.mIdeal.clear();
  constraints.mResizeMode.mIdeal.insert(resizeModeString);

  nsresult rv = StartCapture(constraints, resizeMode);
  mSettingsUpdatedByFrame->mValue = false;
  return rv;
}

nsresult MediaEngineRemoteVideoSource::StartCapture(
    const NormalizedConstraints& aConstraints,
    const dom::VideoResizeModeEnum& aResizeMode) {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  MOZ_ASSERT(mState == kStarted);

  if (camera::GetChildAndCall(&camera::CamerasChild::StartCapture, mCapEngine,
                              mCaptureId, mCapability, aConstraints,
                              aResizeMode, this)) {
    LOG("StartCapture failed");
    MutexAutoLock lock(mMutex);
    mState = kStopped;
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

nsresult MediaEngineRemoteVideoSource::FocusOnSelectedSource() {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  int result;
  result = camera::GetChildAndCall(&camera::CamerasChild::FocusOnSelectedSource,
                                   mCapEngine, mCaptureId);
  return result == 0 ? NS_OK : NS_ERROR_FAILURE;
}

nsresult MediaEngineRemoteVideoSource::Stop() {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  if (mState == kStopped || mState == kAllocated) {
    return NS_OK;
  }

  MOZ_ASSERT(mState == kStarted);

  int error = camera::GetChildAndCall(&camera::CamerasChild::StopCapture,
                                      mCapEngine, mCaptureId);

  if (error == camera::kError) {
    // CamerasParent replied with error. The capturer is still running.
    return NS_ERROR_FAILURE;
  }

  {
    MutexAutoLock lock(mMutex);
    mState = kStopped;
  }

  if (error == camera::kSuccess) {
    return NS_OK;
  }

  MOZ_ASSERT(error == camera::kIpcError);
  // Failure can occur when the parent process is shutting down, and the IPC
  // channel is down. We still consider the capturer stopped in this case,
  // since it cannot deliver frames without the IPC channel open.
  return NS_ERROR_FAILURE;
}

nsresult MediaEngineRemoteVideoSource::Reconfigure(
    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    const char** aOutBadConstraint) {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  NormalizedConstraints c(aConstraints);
  const auto resizeMode = MediaConstraintsHelper::GetResizeMode(c, aPrefs);
  const auto legacyDistanceMode =
      mCapEngine == camera::CameraEngine ? kFitness : kFeasibility;
  const auto distanceMode =
      resizeMode.map(&ToDistanceCalculation).valueOr(legacyDistanceMode);
  webrtc::CaptureCapability newCapability;
  LOG("ChooseCapability({}) for mTargetCapability (Reconfigure) ++",
      ToString(distanceMode));
  if (!ChooseCapability(c, aPrefs, newCapability, distanceMode,
                        aOutBadConstraint)) {
    if (aOutBadConstraint && !*aOutBadConstraint) {
      *aOutBadConstraint =
          MediaConstraintsHelper::FindBadConstraint(c, aPrefs, mMediaDevice);
    }
    return NS_ERROR_INVALID_ARG;
  }
  LOG("ChooseCapability({}) for mTargetCapability (Reconfigure) --",
      ToString(distanceMode));

  bool needsRestart{};
  DesiredSizeInput input{};
  double framerate = 0.0;
  {
    MutexAutoLock lock(mMutex);

    needsRestart = mCapability != newCapability || mConstraints != Some(c) ||
                   !(*mPrefs == aPrefs);

    // StartCapture() applies mCapability on the device.
    mCapability = std::move(newCapability);
    mCalculation = distanceMode;
    mConstraints = Some(c);
    *mPrefs = aPrefs;
    const int32_t& cw = mCapability.width;
    const int32_t& ch = mCapability.height;
    input = {
        .mConstraints = c,
        .mCanCropAndScale = resizeMode.map([](auto aRM) {
          return aRM == dom::VideoResizeModeEnum::Crop_and_scale;
        }),
        .mCapabilityWidth = cw ? Some(cw) : Nothing(),
        .mCapabilityHeight = ch ? Some(ch) : Nothing(),
        .mCapEngine = mCapEngine,
        .mInputWidth = cw ? cw : mIncomingImageSize.width,
        .mInputHeight = ch ? ch : mIncomingImageSize.height,
        .mRotation = 0,
    };
    framerate = distanceMode == kFeasibility
                    ? std::min(mConstraints->mFrameRate.Get(mCapability.maxFPS),
                               AssertedCast<double>(mCapability.maxFPS))
                    : mCapability.maxFPS;
  }

  if (mState == kStarted && needsRestart) {
    nsresult rv =
        StartCapture(c, resizeMode.valueOr(dom::VideoResizeModeEnum::None));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      LOG("Video source {} for video device {} Reconfigure() failed "
          "unexpectedly in Start(). rv={}",
          fmt::ptr(this), mCaptureId, rv);
      return NS_ERROR_UNEXPECTED;
    }
  }

  mSettingsUpdatedByFrame->mValue = false;
  gfx::IntSize dstSize = CalculateDesiredSize(input);
  NS_DispatchToMainThread(NS_NewRunnableFunction(
      __func__, [settings = mSettings, updated = mSettingsUpdatedByFrame,
                 dstSize, framerate, resizeMode]() mutable {
        if (!updated->mValue) {
          settings->mWidth.Value() = dstSize.width;
          settings->mHeight.Value() = dstSize.height;
        }
        settings->mFrameRate.Value() = framerate;
        if (resizeMode) {
          settings->mResizeMode.Reset();
          settings->mResizeMode.Construct(
              NS_ConvertASCIItoUTF16(dom::GetEnumString(*resizeMode)));
        }
      }));

  return NS_OK;
}

size_t MediaEngineRemoteVideoSource::NumCapabilities() const {
  AssertIsOnOwningThread();

  if (!mCapabilities.IsEmpty()) {
    return mCapabilities.Length();
  }

  int num = camera::GetChildAndCall(&camera::CamerasChild::NumberOfCapabilities,
                                    mCapEngine, mDeviceUUID.get());
  if (num > 0) {
    mCapabilities.SetLength(num);
  } else {
    // The default for devices that don't return discrete capabilities: treat
    // them as supporting all capabilities orthogonally. E.g. screensharing.
    // CaptureCapability defaults key values to 0, which means accept any value.
    mCapabilities.AppendElement(MakeUnique<webrtc::CaptureCapability>());
    mCapabilitiesAreHardcoded = true;
  }

  return mCapabilities.Length();
}

webrtc::CaptureCapability& MediaEngineRemoteVideoSource::GetCapability(
    size_t aIndex) const {
  AssertIsOnOwningThread();
  MOZ_RELEASE_ASSERT(aIndex < mCapabilities.Length());
  if (!mCapabilities[aIndex]) {
    mCapabilities[aIndex] = MakeUnique<webrtc::CaptureCapability>();
    camera::GetChildAndCall(&camera::CamerasChild::GetCaptureCapability,
                            mCapEngine, mDeviceUUID.get(), aIndex,
                            mCapabilities[aIndex].get());
  }
  return *mCapabilities[aIndex];
}

const TrackingId& MediaEngineRemoteVideoSource::GetTrackingId() const {
  AssertIsOnOwningThread();
  MOZ_ASSERT(mState != kReleased);
  return mTrackingId;
}

void MediaEngineRemoteVideoSource::OnCaptureEnded() {
  mFirstFramePromiseHolder.RejectIfExists(NS_ERROR_UNEXPECTED, __func__);
  mCaptureEndedEvent.Notify();
}

int MediaEngineRemoteVideoSource::DeliverFrame(
    uint8_t* aBuffer, const camera::VideoFrameProperties& aProps) {
  // Cameras IPC thread - take great care with accessing members!

  DesiredSizeInput input{};
  {
    MutexAutoLock lock(mMutex);
    MOZ_ASSERT(mState == kStarted);
    mIncomingImageSize = {aProps.width(), aProps.height()};
    const int32_t& cw = mCapability.width;
    const int32_t& ch = mCapability.height;

    input = {
        .mConstraints = *mConstraints,
        .mCanCropAndScale = mPrefs->mResizeModeEnabled
                                ? Some(mCalculation == kFeasibility)
                                : Nothing(),
        .mCapabilityWidth = cw ? Some(cw) : Nothing(),
        .mCapabilityHeight = ch ? Some(ch) : Nothing(),
        .mCapEngine = mCapEngine,
        .mInputWidth = aProps.width(),
        .mInputHeight = aProps.height(),
        .mRotation = aProps.rotation(),
    };
    if (!mFrameDeliveringTrackingId) {
      mFrameDeliveringTrackingId = Some(mTrackingId);
    }
  }

  gfx::IntSize dstSize = CalculateDesiredSize(input);

  std::function<void()> callback_unused = []() {};
  webrtc::scoped_refptr<webrtc::I420BufferInterface> buffer =
      webrtc::WrapI420Buffer(
          aProps.width(), aProps.height(), aBuffer, aProps.yStride(),
          aBuffer + aProps.yAllocatedSize(), aProps.uStride(),
          aBuffer + aProps.yAllocatedSize() + aProps.uAllocatedSize(),
          aProps.vStride(), callback_unused);

  if ((dstSize.width != aProps.width() || dstSize.height != aProps.height()) &&
      dstSize.width <= aProps.width() && dstSize.height <= aProps.height()) {
    PerformanceRecorder<CopyVideoStage> rec("MERVS::CropAndScale"_ns,
                                            *mFrameDeliveringTrackingId,
                                            dstSize.width, dstSize.height);
    // Destination resolution is smaller than source buffer. We'll rescale.
    webrtc::scoped_refptr<webrtc::I420Buffer> scaledBuffer =
        mRescalingBufferPool.CreateI420Buffer(dstSize.width, dstSize.height);
    if (!scaledBuffer) {
      MOZ_ASSERT_UNREACHABLE(
          "We might fail to allocate a buffer, but with this "
          "being a recycling pool that shouldn't happen");
      return 0;
    }
    scaledBuffer->CropAndScaleFrom(*buffer);
    buffer = std::move(scaledBuffer);
    rec.Record();
  }

  layers::PlanarYCbCrData data;
  data.mYChannel = const_cast<uint8_t*>(buffer->DataY());
  data.mYStride = buffer->StrideY();
  MOZ_ASSERT(buffer->StrideU() == buffer->StrideV());
  data.mCbCrStride = buffer->StrideU();
  data.mCbChannel = const_cast<uint8_t*>(buffer->DataU());
  data.mCrChannel = const_cast<uint8_t*>(buffer->DataV());
  data.mPictureRect = gfx::IntRect(00, buffer->width(), buffer->height());
  data.mYUVColorSpace = gfx::YUVColorSpace::BT601;
  data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;

  RefPtr<layers::PlanarYCbCrImage> image;
  {
    PerformanceRecorder<CopyVideoStage> rec("MERVS::Copy"_ns,
                                            *mFrameDeliveringTrackingId,
                                            dstSize.width, dstSize.height);
    image = mImageContainer->CreatePlanarYCbCrImage();
    if (NS_FAILED(image->CopyData(data))) {
      MOZ_ASSERT_UNREACHABLE(
          "We might fail to allocate a buffer, but with this "
          "being a recycling container that shouldn't happen");
      return 0;
    }
    rec.Record();
  }

#ifdef DEBUG
  static uint32_t frame_num = 0;
  LOG_FRAME(
      "frame {} ({}x{})->({}x{}); rotation {}, rtpTimeStamp {}, ntpTimeMs "
      "{}, renderTimeMs {}",
      frame_num++, aProps.width(), aProps.height(), dstSize.width,
      dstSize.height, aProps.rotation(), aProps.rtpTimeStamp(),
      aProps.ntpTimeMs(), aProps.renderTimeMs());
#endif

  if (mScaledImageSize != dstSize) {
    NS_DispatchToMainThread(NS_NewRunnableFunction(
        "MediaEngineRemoteVideoSource::FrameSizeChange",
        [settings = mSettings, updated = mSettingsUpdatedByFrame,
         holder = std::move(mFirstFramePromiseHolder), dstSize]() mutable {
          settings->mWidth.Value() = dstSize.width;
          settings->mHeight.Value() = dstSize.height;
          updated->mValue = true;
          // Since mImageSize was initialized to (0,0), we end up here on the
          // arrival of the first frame. We resolve the promise representing
          // arrival of first frame, after correct settings values have been
          // made available (Resolve() is idempotent if already resolved).
          holder.ResolveIfExists(true, __func__);
        }));
  }

  {
    MutexAutoLock lock(mMutex);
    MOZ_ASSERT(mState == kStarted);
    VideoSegment segment;
    mScaledImageSize = image->GetSize();
    segment.AppendWebrtcLocalFrame(image.forget(), mScaledImageSize, mPrincipal,
                                   /* aForceBlack */ false, TimeStamp::Now(),
                                   aProps.captureTime());
    mTrack->AppendData(&segment);
  }

  return 0;
}

uint32_t MediaEngineRemoteVideoSource::GetDistance(
    const webrtc::CaptureCapability& aCandidate,
    const NormalizedConstraintSet& aConstraints,
    const DistanceCalculation aCalculate) const {
  if (aCalculate == kFeasibility) {
    return GetFeasibilityDistance(aCandidate, aConstraints);
  }
  return GetFitnessDistance(aCandidate, aConstraints);
}

uint32_t MediaEngineRemoteVideoSource::GetFitnessDistance(
    const webrtc::CaptureCapability& aCandidate,
    const NormalizedConstraintSet& aConstraints) const {
  AssertIsOnOwningThread();

  // Treat width|height|frameRate == 0 on capability as "can do any".
  // This allows for orthogonal capabilities that are not in discrete steps.

  typedef MediaConstraintsHelper H;
  uint64_t distance =
      uint64_t(H::FitnessDistance(mFacingMode, aConstraints.mFacingMode)) +
      uint64_t(aCandidate.width ? H::FitnessDistance(int32_t(aCandidate.width),
                                                     aConstraints.mWidth)
                                : 0) +
      uint64_t(aCandidate.height
                   ? H::FitnessDistance(int32_t(aCandidate.height),
                                        aConstraints.mHeight)
                   : 0) +
      uint64_t(aCandidate.maxFPS ? H::FitnessDistance(double(aCandidate.maxFPS),
                                                      aConstraints.mFrameRate)
                                 : 0);
  return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
}

uint32_t MediaEngineRemoteVideoSource::GetFeasibilityDistance(
    const webrtc::CaptureCapability& aCandidate,
    const NormalizedConstraintSet& aConstraints) const {
  AssertIsOnOwningThread();

  // Treat width|height|frameRate == 0 on capability as "can do any".
  // This allows for orthogonal capabilities that are not in discrete steps.

  typedef MediaConstraintsHelper H;
  uint64_t distance =
      uint64_t(H::FitnessDistance(mFacingMode, aConstraints.mFacingMode)) +
      uint64_t(aCandidate.width
                   ? H::FeasibilityDistance(int32_t(aCandidate.width),
                                            aConstraints.mWidth)
                   : 0) +
      uint64_t(aCandidate.height
                   ? H::FeasibilityDistance(int32_t(aCandidate.height),
                                            aConstraints.mHeight)
                   : 0) +
      uint64_t(aCandidate.maxFPS
                   ? H::FeasibilityDistance(double(aCandidate.maxFPS),
                                            aConstraints.mFrameRate)
                   : 0);
  return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
}

// Find best capability by removing inferiors. May leave >1 of equal distance

/* static */
void MediaEngineRemoteVideoSource::TrimLessFitCandidates(
    nsTArray<CapabilityCandidate>& aSet) {
  uint32_t best = UINT32_MAX;
  for (auto& candidate : aSet) {
    if (best > candidate.mDistance) {
      best = candidate.mDistance;
    }
  }
  aSet.RemoveElementsBy(
      [best](const auto& set) { return set.mDistance > best; });
  MOZ_ASSERT(aSet.Length());
}

uint32_t MediaEngineRemoteVideoSource::GetBestFitnessDistance(
    const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
    const MediaEnginePrefs& aPrefs) const {
  AssertIsOnOwningThread();

  size_t num = NumCapabilities();
  nsTArray<CapabilityCandidate> candidateSet;
  for (size_t i = 0; i < num; i++) {
    candidateSet.AppendElement(CapabilityCandidate(GetCapability(i)));
  }

  bool first = true;
  for (const NormalizedConstraintSet* ns : aConstraintSets) {
    auto mode = MediaConstraintsHelper::GetResizeMode(*ns, aPrefs)
                    .map(&ToDistanceCalculation)
                    .valueOr(kFitness);
    for (size_t i = 0; i < candidateSet.Length();) {
      auto& candidate = candidateSet[i];
      uint32_t distance = GetDistance(candidate.mCapability, *ns, mode);
      if (distance == UINT32_MAX) {
        candidateSet.RemoveElementAt(i);
      } else {
        ++i;
        if (first) {
          candidate.mDistance = distance;
        }
      }
    }
    first = false;
  }
  if (!candidateSet.Length()) {
    return UINT32_MAX;
  }
  TrimLessFitCandidates(candidateSet);
  return candidateSet[0].mDistance;
}

static const char* ConvertVideoTypeToCStr(webrtc::VideoType aType) {
  switch (aType) {
    case webrtc::VideoType::kI420:
      return "I420";
    case webrtc::VideoType::kIYUV:
    case webrtc::VideoType::kYV12:
      return "YV12";
    case webrtc::VideoType::kRGB24:
      return "24BG";
    case webrtc::VideoType::kABGR:
      return "ABGR";
    case webrtc::VideoType::kARGB:
      return "ARGB";
    case webrtc::VideoType::kARGB4444:
      return "R444";
    case webrtc::VideoType::kRGB565:
      return "RGBP";
    case webrtc::VideoType::kARGB1555:
      return "RGBO";
    case webrtc::VideoType::kYUY2:
      return "YUY2";
    case webrtc::VideoType::kUYVY:
      return "UYVY";
    case webrtc::VideoType::kMJPEG:
      return "MJPG";
    case webrtc::VideoType::kNV21:
      return "NV21";
    case webrtc::VideoType::kNV12:
      return "NV12";
    case webrtc::VideoType::kBGRA:
      return "BGRA";
    case webrtc::VideoType::kUnknown:
    default:
      return "unknown";
  }
}

static void LogCapability(const char* aHeader,
                          const webrtc::CaptureCapability& aCapability,
                          uint32_t aDistance) {
  LOG("{}: {:4} x {:4} x {:2} maxFps, {}. Distance = {}", aHeader,
      aCapability.width, aCapability.height, aCapability.maxFPS,
      ConvertVideoTypeToCStr(aCapability.videoType), aDistance);
}

bool MediaEngineRemoteVideoSource::ChooseCapability(
    const NormalizedConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
    webrtc::CaptureCapability& aCapability,
    const DistanceCalculation aCalculate, const char** aOutBadConstraint) {
  LOG("{}", __PRETTY_FUNCTION__);
  AssertIsOnOwningThread();

  if (MOZ_LOG_TEST(gMediaManagerLog, LogLevel::Debug)) {
    LOG("ChooseCapability: prefs: {}x{} @{}fps", aPrefs.GetWidth(),
        aPrefs.GetHeight(), aPrefs.mFPS);
    MediaConstraintsHelper::LogConstraints(aConstraints);
    if (!aConstraints.mAdvanced.empty()) {
      LOG("Advanced array[{}]:", aConstraints.mAdvanced.size());
      for (const auto& advanced : aConstraints.mAdvanced) {
        MediaConstraintsHelper::LogConstraints(advanced);
      }
    }
  }

  MOZ_ASSERT_IF(aOutBadConstraint, !*aOutBadConstraint);
  FlattenedConstraints c(aConstraints);
  const auto checkConstraint = [](const auto& aConstraint) {
    return aConstraint.mMin <= aConstraint.mMax && aConstraint.mMax > 0;
  };
  if (!checkConstraint(c.mWidth)) {
    if (aOutBadConstraint) {
      *aOutBadConstraint = "width";
    }
    return false;
  }
  if (!checkConstraint(c.mHeight)) {
    if (aOutBadConstraint) {
      *aOutBadConstraint = "height";
    }
    return false;
  }
  if (!checkConstraint(c.mFrameRate)) {
    if (aOutBadConstraint) {
      *aOutBadConstraint = "frameRate";
    }
    return false;
  }

  switch (mCapEngine) {
    case camera::ScreenEngine:
    case camera::WinEngine:
    case camera::BrowserEngine: {
      // DesktopCaptureImpl polls for frames and so must know the framerate to
      // capture at. This is signaled through CamerasParent as the capability's
      // maxFPS. Note that DesktopCaptureImpl does not expose any capabilities.
      constexpr int32_t probablyNativeFramerate = 60;
      constexpr int32_t cap = 120;
      const int32_t constrainedFramerate =
          SaturatingCast<int32_t>(std::lround(c.mFrameRate.Get(aPrefs.mFPS)));
      aCapability.maxFPS =
          aCalculate == kFeasibility
              ? std::min(constrainedFramerate, cap)
              : std::clamp(constrainedFramerate, probablyNativeFramerate, cap);
      return true;
    }
    default:
      break;
  }

  nsTArray<CapabilityCandidate> candidateSet;
  size_t num = NumCapabilities();
  int32_t maxHeight = 0, maxWidth = 0, maxFps = 0;
  for (size_t i = 0; i < num; i++) {
    auto capability = GetCapability(i);
    if (capability.height > maxHeight) {
      maxHeight = capability.height;
    }
    if (capability.width > maxWidth) {
      maxWidth = capability.width;
    }
    if (capability.maxFPS > maxFps) {
      maxFps = capability.maxFPS;
    }
    candidateSet.AppendElement(CapabilityCandidate(capability));
  }

  NS_DispatchToMainThread(NS_NewRunnableFunction(
      "MediaEngineRemoteVideoSource::ChooseCapability",
      [capabilities = mTrackCapabilities, maxHeight, maxWidth,
       maxFps]() mutable {
        dom::ULongRange widthRange;
        widthRange.mMax.Construct(maxWidth);
        widthRange.mMin.Construct(2);
        capabilities->mWidth.Reset();
        capabilities->mWidth.Construct(widthRange);

        dom::ULongRange heightRange;
        heightRange.mMax.Construct(maxHeight);
        heightRange.mMin.Construct(2);
        capabilities->mHeight.Reset();
        capabilities->mHeight.Construct(heightRange);

        dom::DoubleRange frameRateRange;
        frameRateRange.mMax.Construct(maxFps);
        frameRateRange.mMin.Construct(0);
        capabilities->mFrameRate.Reset();
        capabilities->mFrameRate.Construct(frameRateRange);
      }));

  if (mCapabilitiesAreHardcoded && mCapEngine == camera::CameraEngine) {
    // We have a hardcoded capability, which means this camera didn't report
    // discrete capabilities. It might still allow a ranged capability, so we
    // add a couple of default candidates based on prefs and constraints.
    // The chosen candidate will be propagated to StartCapture() which will fail
    // for an invalid candidate.
    MOZ_DIAGNOSTIC_ASSERT(mCapabilities.Length() == 1);
    MOZ_DIAGNOSTIC_ASSERT(candidateSet.Length() == 1);
    candidateSet.Clear();

    FlattenedConstraints c(aConstraints);
    // Reuse the code across both the low-definition (`false`) pref and
    // the high-definition (`true`) pref.
    // If there are constraints we try to satisfy them but we default to prefs.
    // Note that since constraints are from content and can literally be
    // anything we put (rather generous) caps on them.
    for (bool isHd : {false, true}) {
      webrtc::CaptureCapability cap;
      int32_t prefWidth = aPrefs.GetWidth(isHd);
      int32_t prefHeight = aPrefs.GetHeight(isHd);

      cap.width = c.mWidth.Get(prefWidth);
      cap.width = std::clamp(cap.width, 07680);

      cap.height = c.mHeight.Get(prefHeight);
      cap.height = std::clamp(cap.height, 04320);

      cap.maxFPS =
          SaturatingCast<int32_t>(std::lround(c.mFrameRate.Get(aPrefs.mFPS)));
      cap.maxFPS = std::clamp(cap.maxFPS, 0480);

      if (cap.width != prefWidth) {
        // Width was affected by constraints.
        // We'll adjust the height too so the aspect ratio is retained.
        cap.height = cap.width * prefHeight / prefWidth;
      } else if (cap.height != prefHeight) {
        // Height was affected by constraints but not width.
        // We'll adjust the width too so the aspect ratio is retained.
        cap.width = cap.height * prefWidth / prefHeight;
      }

      if (candidateSet.Contains(cap, CapabilityComparator())) {
        continue;
      }
      LogCapability("Hardcoded capability", cap, 0);
      candidateSet.AppendElement(cap);
    }
  }

  // First, filter capabilities by required constraints (min, max, exact).

  for (size_t i = 0; i < candidateSet.Length();) {
    auto& candidate = candidateSet[i];
    candidate.mDistance =
        GetDistance(candidate.mCapability, aConstraints, aCalculate);
    LogCapability("Capability", candidate.mCapability, candidate.mDistance);
    if (candidate.mDistance == UINT32_MAX) {
      candidateSet.RemoveElementAt(i);
    } else {
      ++i;
    }
  }

  if (candidateSet.IsEmpty()) {
    LOG("failed to find capability match from {} choices",
        candidateSet.Length());
    return false;
  }

  // Filter further with all advanced constraints (that don't overconstrain).

  for (const auto& cs : aConstraints.mAdvanced) {
    nsTArray<CapabilityCandidate> rejects;
    for (size_t i = 0; i < candidateSet.Length();) {
      if (GetDistance(candidateSet[i].mCapability, cs, aCalculate) ==
          UINT32_MAX) {
        rejects.AppendElement(candidateSet[i]);
        candidateSet.RemoveElementAt(i);
      } else {
        ++i;
      }
    }
    if (!candidateSet.Length()) {
      candidateSet.AppendElements(std::move(rejects));
    }
  }
  MOZ_ASSERT(
      candidateSet.Length(),
      "advanced constraints filtering step can't reduce candidates to zero");

  // Remaining algorithm is up to the UA.

  TrimLessFitCandidates(candidateSet);

  // Any remaining multiples all have the same distance. A common case of this
  // occurs when no ideal is specified. Lean toward defaults.
  uint32_t sameDistance = candidateSet[0].mDistance;
  {
    MediaTrackConstraintSet prefs;
    prefs.mWidth.Construct().SetAsLong() = aPrefs.GetWidth();
    prefs.mHeight.Construct().SetAsLong() = aPrefs.GetHeight();
    prefs.mFrameRate.Construct().SetAsDouble() = aPrefs.mFPS;
    NormalizedConstraintSet normPrefs(prefs, false);

    for (auto& candidate : candidateSet) {
      candidate.mDistance =
          GetDistance(candidate.mCapability, normPrefs, aCalculate);
    }
    TrimLessFitCandidates(candidateSet);
  }

  aCapability = candidateSet[0].mCapability;

  LogCapability("Chosen capability", aCapability, sameDistance);
  return true;
}

void MediaEngineRemoteVideoSource::GetSettings(
    MediaTrackSettings& aOutSettings) const {
  aOutSettings = *mSettings;
}

void MediaEngineRemoteVideoSource::GetCapabilities(
    dom::MediaTrackCapabilities& aOutCapabilities) const {
  aOutCapabilities = *mTrackCapabilities;
}

}  // namespace mozilla

Messung V0.5 in Prozent
C=90 H=95 G=92

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.26Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-25) ¤

*Eine klare Vorstellung vom Zielzustand






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