Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/systemservices/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 50 kB image not shown  

Quelle  CamerasParent.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* 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 "CamerasParent.h"

#include <atomic>
#include "CamerasTypes.h"
#include "MediaEngineSource.h"
#include "PerformanceRecorder.h"
#include "VideoEngine.h"
#include "VideoFrameUtils.h"

#include "common/browser_logging/WebRtcLog.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Assertions.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/Unused.h"
#include "mozilla/Services.h"
#include "mozilla/Logging.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/media/MediaUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_permissions.h"
#include "nsIPermissionManager.h"
#include "nsIThread.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "video_engine/desktop_capture_impl.h"
#include "video_engine/video_capture_factory.h"

#include "api/video/video_frame_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"

#if defined(_WIN32)
#  include <process.h>
#  define getpid() _getpid()
#endif

#undef LOG
#undef LOG_VERBOSE
#undef LOG_ENABLED
mozilla::LazyLogModule gCamerasParentLog("CamerasParent");
#define LOG(...) \
  MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG_FUNCTION()                                 \
  MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, \
          ("CamerasParent(%p)::%s"this, __func__))
#define LOG_VERBOSE(...) \
  MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Verbose, (__VA_ARGS__))
#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)

namespace mozilla {
using media::ShutdownBlockingTicket;
namespace camera {

MOZ_RUNINIT std::map<uint32_t, const char*> sDeviceUniqueIDs;
MOZ_RUNINIT std::map<uint32_t, webrtc::VideoCaptureCapability>
    sAllRequestedCapabilities;

uint32_t ResolutionFeasibilityDistance(int32_t candidate, int32_t requested) {
  // The purpose of this function is to find a smallest resolution
  // which is larger than all requested capabilities.
  // Then we can use down-scaling to fulfill each request.

  MOZ_DIAGNOSTIC_ASSERT(candidate >= 0, "Candidate unexpectedly negative");
  MOZ_DIAGNOSTIC_ASSERT(requested >= 0, "Requested unexpectedly negative");

  if (candidate == 0) {
    // Treat width|height capability of 0 as "can do any".
    // This allows for orthogonal capabilities that are not in discrete steps.
    return 0;
  }

  uint32_t distance =
      std::abs(candidate - requested) * 1000 / std::max(candidate, requested);
  if (candidate >= requested) {
    // This is a good case, the candidate covers the requested resolution.
    return distance;
  }

  // This is a bad case, the candidate is lower than the requested resolution.
  // This is penalized with an added weight of 10000.
  return 10000 + distance;
}

uint32_t FeasibilityDistance(int32_t candidate, int32_t requested) {
  MOZ_DIAGNOSTIC_ASSERT(candidate >= 0, "Candidate unexpectedly negative");
  MOZ_DIAGNOSTIC_ASSERT(requested >= 0, "Requested unexpectedly negative");

  if (candidate == 0) {
    // Treat maxFPS capability of 0 as "can do any".
    // This allows for orthogonal capabilities that are not in discrete steps.
    return 0;
  }

  return std::abs(candidate - requested) * 1000 /
         std::max(candidate, requested);
}

class CamerasParent::VideoEngineArray
    : public media::Refcountable<nsTArray<RefPtr<VideoEngine>>> {};

// Singleton video engines. The sEngines RefPtr is IPC background thread only
// and outlives the CamerasParent instances. The array elements are video
// capture thread only.
using VideoEngineArray = CamerasParent::VideoEngineArray;
static StaticRefPtr<VideoEngineArray> sEngines;
// Number of CamerasParents instances in the current process for which
// mVideoCaptureThread has been set. IPC background thread only.
static int32_t sNumCamerasParents = 0;
// Video processing thread - where webrtc.org capturer code runs. Outlives the
// CamerasParent instances. IPC background thread only.
static StaticRefPtr<nsIThread> sVideoCaptureThread;
// Main VideoCaptureFactory used to create and manage all capture related
// objects. IPC background thread only. Outlives the CamerasParent instances.
static StaticRefPtr<VideoCaptureFactory> sVideoCaptureFactory;

static VideoCaptureFactory* EnsureVideoCaptureFactory() {
  ipc::AssertIsOnBackgroundThread();

  if (sVideoCaptureFactory) {
    return sVideoCaptureFactory;
  }

  sVideoCaptureFactory = MakeRefPtr<VideoCaptureFactory>();
  NS_DispatchToMainThread(
      NS_NewRunnableFunction("CamerasParent::EnsureVideoCaptureFactory",
                             []() { ClearOnShutdown(&sVideoCaptureFactory); }));
  return sVideoCaptureFactory;
}

static already_AddRefed<nsISerialEventTarget>
MakeAndAddRefVideoCaptureThreadAndSingletons() {
  ipc::AssertIsOnBackgroundThread();

  MOZ_ASSERT_IF(sVideoCaptureThread, sNumCamerasParents > 0);
  MOZ_ASSERT_IF(!sVideoCaptureThread, sNumCamerasParents == 0);

  if (!sVideoCaptureThread) {
    LOG("Spinning up WebRTC Cameras Thread");
    nsIThreadManager::ThreadCreationOptions options;
#ifdef XP_WIN
    // Windows desktop capture needs a UI thread
    options.isUiThread = true;
#endif
    nsCOMPtr<nsIThread> videoCaptureThread;
    if (NS_FAILED(NS_NewNamedThread("VideoCapture",
                                    getter_AddRefs(videoCaptureThread), nullptr,
                                    options))) {
      return nullptr;
    }
    sVideoCaptureThread = videoCaptureThread.forget();

    sEngines = MakeRefPtr<VideoEngineArray>();
    sEngines->AppendElements(CaptureEngine::MaxEngine);
  }

  ++sNumCamerasParents;
  return do_AddRef(sVideoCaptureThread);
}

static void ReleaseVideoCaptureThreadAndSingletons() {
  ipc::AssertIsOnBackgroundThread();

  if (--sNumCamerasParents > 0) {
    // Other CamerasParent instances are using the singleton classes.
    return;
  }

  MOZ_ASSERT(sNumCamerasParents == 0, "Double release!");

  // No other CamerasParent instances alive. Clean up.
  LOG("Shutting down VideoEngines and the VideoCapture thread");
  MOZ_ALWAYS_SUCCEEDS(sVideoCaptureThread->Dispatch(
      NS_NewRunnableFunction(__func__, [engines = RefPtr(sEngines.forget())] {
        for (RefPtr<VideoEngine>& engine : *engines) {
          if (engine) {
            VideoEngine::Delete(engine);
            engine = nullptr;
          }
        }
      })));

  MOZ_ALWAYS_SUCCEEDS(RefPtr(sVideoCaptureThread.forget())->AsyncShutdown());
}

// 3 threads are involved in this code:
// - the main thread for some setups, and occassionally for video capture setup
//   calls that don't work correctly elsewhere.
// - the IPC thread on which PBackground is running and which receives and
//   sends messages
// - a thread which will execute the actual (possibly slow) camera access
//   called "VideoCapture". On Windows this is a thread with an event loop
//   suitable for UI access.

void CamerasParent::OnDeviceChange() {
  LOG_FUNCTION();

  mPBackgroundEventTarget->Dispatch(
      NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)]() {
        if (IsShuttingDown()) {
          LOG("OnDeviceChanged failure: parent shutting down.");
          return;
        }
        Unused << SendDeviceChange();
      }));
};

class DeliverFrameRunnable : public mozilla::Runnable {
 public:
  DeliverFrameRunnable(CamerasParent* aParent, CaptureEngine aEngine,
                       uint32_t aStreamId, const TrackingId& aTrackingId,
                       const webrtc::VideoFrame& aFrame,
                       const VideoFrameProperties& aProperties)
      : Runnable("camera::DeliverFrameRunnable"),
        mParent(aParent),
        mCapEngine(aEngine),
        mStreamId(aStreamId),
        mTrackingId(aTrackingId),
        mProperties(aProperties),
        mResult(0) {
    // No ShmemBuffer (of the right size) was available, so make an
    // extra buffer here.  We have no idea when we are going to run and
    // it will be potentially long after the webrtc frame callback has
    // returned, so the copy needs to be no later than here.
    // We will need to copy this back into a Shmem later on so we prefer
    // using ShmemBuffers to avoid the extra copy.
    PerformanceRecorder<CopyVideoStage> rec(
        "CamerasParent::VideoFrameToAltBuffer"_ns, aTrackingId, aFrame.width(),
        aFrame.height());
    mAlternateBuffer.reset(new unsigned char[aProperties.bufferSize()]);
    VideoFrameUtils::CopyVideoFrameBuffers(mAlternateBuffer.get(),
                                           aProperties.bufferSize(), aFrame);
    rec.Record();
  }

  DeliverFrameRunnable(CamerasParent* aParent, CaptureEngine aEngine,
                       uint32_t aStreamId, const TrackingId& aTrackingId,
                       ShmemBuffer aBuffer, VideoFrameProperties& aProperties)
      : Runnable("camera::DeliverFrameRunnable"),
        mParent(aParent),
        mCapEngine(aEngine),
        mStreamId(aStreamId),
        mTrackingId(aTrackingId),
        mBuffer(std::move(aBuffer)),
        mProperties(aProperties),
        mResult(0) {};

  NS_IMETHOD Run() override {
    // runs on BackgroundEventTarget
    MOZ_ASSERT(GetCurrentSerialEventTarget() ==
               mParent->mPBackgroundEventTarget);
    if (mParent->IsShuttingDown()) {
      // Communication channel is being torn down
      mResult = 0;
      return NS_OK;
    }
    if (!mParent->DeliverFrameOverIPC(mCapEngine, mStreamId, mTrackingId,
                                      std::move(mBuffer),
                                      mAlternateBuffer.get(), mProperties)) {
      mResult = -1;
    } else {
      mResult = 0;
    }
    return NS_OK;
  }

  int GetResult() { return mResult; }

 private:
  const RefPtr<CamerasParent> mParent;
  const CaptureEngine mCapEngine;
  const uint32_t mStreamId;
  const TrackingId mTrackingId;
  ShmemBuffer mBuffer;
  UniquePtr<unsigned char[]> mAlternateBuffer;
  const VideoFrameProperties mProperties;
  int mResult;
};

int CamerasParent::DeliverFrameOverIPC(CaptureEngine aCapEngine,
                                       uint32_t aStreamId,
                                       const TrackingId& aTrackingId,
                                       ShmemBuffer aBuffer,
                                       unsigned char* aAltBuffer,
                                       const VideoFrameProperties& aProps) {
  // No ShmemBuffers were available, so construct one now of the right size
  // and copy into it. That is an extra copy, but we expect this to be
  // the exceptional case, because we just assured the next call *will* have a
  // buffer of the right size.
  if (aAltBuffer != nullptr) {
    // Get a shared memory buffer from the pool, at least size big
    ShmemBuffer shMemBuff = mShmemPool.Get(this, aProps.bufferSize());

    if (!shMemBuff.Valid()) {
      LOG("No usable Video shmem in DeliverFrame (out of buffers?)");
      // We can skip this frame if we run out of buffers, it's not a real error.
      return 0;
    }

    PerformanceRecorder<CopyVideoStage> rec(
        "CamerasParent::AltBufferToShmem"_ns, aTrackingId, aProps.width(),
        aProps.height());
    // get() and Size() check for proper alignment of the segment
    memcpy(shMemBuff.GetBytes(), aAltBuffer, aProps.bufferSize());
    rec.Record();

    if (!SendDeliverFrame(aStreamId, std::move(shMemBuff.Get()), aProps)) {
      return -1;
    }
  } else {
    MOZ_ASSERT(aBuffer.Valid());
    // ShmemBuffer was available, we're all good. A single copy happened
    // in the original webrtc callback.
    if (!SendDeliverFrame(aStreamId, std::move(aBuffer.Get()), aProps)) {
      return -1;
    }
  }

  return 0;
}

ShmemBuffer CamerasParent::GetBuffer(size_t aSize) {
  return mShmemPool.GetIfAvailable(aSize);
}

void CallbackHelper::OnCaptureEnded() {
  nsIEventTarget* target = mParent->GetBackgroundEventTarget();

  MOZ_ALWAYS_SUCCEEDS(target->Dispatch(NS_NewRunnableFunction(
      __func__, [&] { Unused << mParent->SendCaptureEnded(mStreamId); })));
}

void CallbackHelper::OnFrame(const webrtc::VideoFrame& aVideoFrame) {
  LOG_VERBOSE("CamerasParent(%p)::%s", mParent, __func__);
  if (profiler_thread_is_being_profiled_for_markers()) {
    PROFILER_MARKER_UNTYPED(
        nsPrintfCString("CaptureVideoFrame %dx%d %s %s", aVideoFrame.width(),
                        aVideoFrame.height(),
                        webrtc::VideoFrameBufferTypeToString(
                            aVideoFrame.video_frame_buffer()->type()),
                        mTrackingId.ToString().get()),
        MEDIA_RT);
  }
  RefPtr<DeliverFrameRunnable> runnable = nullptr;
  // Get frame properties
  camera::VideoFrameProperties properties;
  VideoFrameUtils::InitFrameBufferProperties(aVideoFrame, properties);
  // Get a shared memory buffer to copy the frame data into
  ShmemBuffer shMemBuffer = mParent->GetBuffer(properties.bufferSize());
  if (!shMemBuffer.Valid()) {
    // Either we ran out of buffers or they're not the right size yet
    LOG("Correctly sized Video shmem not available in DeliverFrame");
    // We will do the copy into a(n extra) temporary buffer inside
    // the DeliverFrameRunnable constructor.
  } else {
    // Shared memory buffers of the right size are available, do the copy here.
    PerformanceRecorder<CopyVideoStage> rec(
        "CamerasParent::VideoFrameToShmem"_ns, mTrackingId, aVideoFrame.width(),
        aVideoFrame.height());
    VideoFrameUtils::CopyVideoFrameBuffers(
        shMemBuffer.GetBytes(), properties.bufferSize(), aVideoFrame);
    rec.Record();
    runnable =
        new DeliverFrameRunnable(mParent, mCapEngine, mStreamId, mTrackingId,
                                 std::move(shMemBuffer), properties);
  }
  if (!runnable) {
    runnable = new DeliverFrameRunnable(mParent, mCapEngine, mStreamId,
                                        mTrackingId, aVideoFrame, properties);
  }
  MOZ_ASSERT(mParent);
  nsIEventTarget* target = mParent->GetBackgroundEventTarget();
  MOZ_ASSERT(target != nullptr);
  target->Dispatch(runnable, NS_DISPATCH_NORMAL);
}

ipc::IPCResult CamerasParent::RecvReleaseFrame(ipc::Shmem&& aShmem) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());

  mShmemPool.Put(ShmemBuffer(aShmem));
  return IPC_OK();
}

void CamerasParent::CloseEngines() {
  MOZ_ASSERT(mVideoCaptureThread->IsOnCurrentThread());
  LOG_FUNCTION();

  // Stop the callers
  while (!mCallbacks.IsEmpty()) {
    auto capEngine = mCallbacks[0]->mCapEngine;
    auto streamNum = mCallbacks[0]->mStreamId;
    LOG("Forcing shutdown of engine %d, capturer %d", capEngine, streamNum);
    StopCapture(capEngine, streamNum);
    Unused << ReleaseCapture(capEngine, streamNum);
  }

  auto device_info = GetDeviceInfo(CameraEngine);
  MOZ_ASSERT(device_info);
  if (device_info) {
    device_info->DeRegisterVideoInputFeedBack(this);
  }
}

std::shared_ptr<webrtc::VideoCaptureModule::DeviceInfo>
CamerasParent::GetDeviceInfo(int aEngine) {
  MOZ_ASSERT(mVideoCaptureThread->IsOnCurrentThread());
  LOG_VERBOSE("CamerasParent(%p)::%s"this, __func__);

  auto* engine = EnsureInitialized(aEngine);
  if (!engine) {
    return nullptr;
  }
  return engine->GetOrCreateVideoCaptureDeviceInfo(this);
}

VideoEngine* CamerasParent::EnsureInitialized(int aEngine) {
  MOZ_ASSERT(mVideoCaptureThread->IsOnCurrentThread());
  LOG_VERBOSE("CamerasParent(%p)::%s"this, __func__);
  CaptureEngine capEngine = static_cast<CaptureEngine>(aEngine);

  if (VideoEngine* engine = mEngines->ElementAt(capEngine); engine) {
    return engine;
  }

  CaptureDeviceType captureDeviceType = CaptureDeviceType::Camera;
  switch (capEngine) {
    case ScreenEngine:
      captureDeviceType = CaptureDeviceType::Screen;
      break;
    case BrowserEngine:
      captureDeviceType = CaptureDeviceType::Browser;
      break;
    case WinEngine:
      captureDeviceType = CaptureDeviceType::Window;
      break;
    case CameraEngine:
      captureDeviceType = CaptureDeviceType::Camera;
      break;
    default:
      LOG("Invalid webrtc Video engine");
      return nullptr;
  }

  RefPtr<VideoEngine> engine =
      VideoEngine::Create(captureDeviceType, mVideoCaptureFactory);
  if (!engine) {
    LOG("VideoEngine::Create failed");
    return nullptr;
  }

  return mEngines->ElementAt(capEngine) = std::move(engine);
}

// Dispatch the runnable to do the camera operation on the
// specific Cameras thread, preventing us from blocking, and
// chain a runnable to send back the result on the IPC thread.
// It would be nice to get rid of the code duplication here,
// perhaps via Promises.
ipc::IPCResult CamerasParent::RecvNumberOfCaptureDevices(
    const CaptureEngine& aCapEngine) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();
  LOG("CaptureEngine=%d", aCapEngine);

  using Promise = MozPromise<intbooltrue>;
  InvokeAsync(mVideoCaptureThread, __func__,
              [this, self = RefPtr(this), aCapEngine] {
                int num = -1;
                if (auto devInfo = GetDeviceInfo(aCapEngine)) {
                  num = static_cast<int>(devInfo->NumberOfDevices());
                }

                return Promise::CreateAndResolve(
                    num, "CamerasParent::RecvNumberOfCaptureDevices");
              })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            int nrDevices = aValue.ResolveValue();

            if (mDestroyed) {
              LOG("RecvNumberOfCaptureDevices failure: child not alive");
              return;
            }

            if (nrDevices < 0) {
              LOG("RecvNumberOfCaptureDevices couldn't find devices");
              Unused << SendReplyFailure();
              return;
            }

            LOG("RecvNumberOfCaptureDevices: %d", nrDevices);
            Unused << SendReplyNumberOfCaptureDevices(nrDevices);
          });
  return IPC_OK();
}

ipc::IPCResult CamerasParent::RecvEnsureInitialized(
    const CaptureEngine& aCapEngine) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();

  using Promise = MozPromise<boolbooltrue>;
  InvokeAsync(mVideoCaptureThread, __func__,
              [this, self = RefPtr(this), aCapEngine] {
                return Promise::CreateAndResolve(
                    EnsureInitialized(aCapEngine),
                    "CamerasParent::RecvEnsureInitialized");
              })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            bool result = aValue.ResolveValue();

            if (mDestroyed) {
              LOG("RecvEnsureInitialized: child not alive");
              return;
            }

            if (!result) {
              LOG("RecvEnsureInitialized failed");
              Unused << SendReplyFailure();
              return;
            }

            LOG("RecvEnsureInitialized succeeded");
            Unused << SendReplySuccess();
          });
  return IPC_OK();
}

ipc::IPCResult CamerasParent::RecvNumberOfCapabilities(
    const CaptureEngine& aCapEngine, const nsACString& aUniqueId) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();
  LOG("Getting caps for %s", PromiseFlatCString(aUniqueId).get());

  using Promise = MozPromise<intbooltrue>;
  InvokeAsync(
      mVideoCaptureThread, __func__,
      [this, self = RefPtr(this), id = nsCString(aUniqueId), aCapEngine]() {
        int num = -1;
        if (auto devInfo = GetDeviceInfo(aCapEngine)) {
          num = devInfo->NumberOfCapabilities(id.get());
        }
        return Promise::CreateAndResolve(
            num, "CamerasParent::RecvNumberOfCapabilities");
      })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            int aNrCapabilities = aValue.ResolveValue();

            if (mDestroyed) {
              LOG("RecvNumberOfCapabilities: child not alive");
              return;
            }

            if (aNrCapabilities < 0) {
              LOG("RecvNumberOfCapabilities couldn't find capabilities");
              Unused << SendReplyFailure();
              return;
            }

            LOG("RecvNumberOfCapabilities: %d", aNrCapabilities);
            Unused << SendReplyNumberOfCapabilities(aNrCapabilities);
          });
  return IPC_OK();
}

ipc::IPCResult CamerasParent::RecvGetCaptureCapability(
    const CaptureEngine& aCapEngine, const nsACString& aUniqueId,
    const int& aIndex) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();
  LOG("RecvGetCaptureCapability: %s %d", PromiseFlatCString(aUniqueId).get(),
      aIndex);

  using Promise = MozPromise<webrtc::VideoCaptureCapability, inttrue>;
  InvokeAsync(mVideoCaptureThread, __func__,
              [this, self = RefPtr(this), id = nsCString(aUniqueId), aCapEngine,
               aIndex] {
                webrtc::VideoCaptureCapability webrtcCaps;
                int error = -1;
                if (auto devInfo = GetDeviceInfo(aCapEngine)) {
                  error = devInfo->GetCapability(id.get(), aIndex, webrtcCaps);
                }

                if (!error && aCapEngine == CameraEngine) {
                  auto iter = mAllCandidateCapabilities.find(id);
                  if (iter == mAllCandidateCapabilities.end()) {
                    std::map<uint32_t, webrtc::VideoCaptureCapability>
                        candidateCapabilities;
                    candidateCapabilities.emplace(aIndex, webrtcCaps);
                    mAllCandidateCapabilities.emplace(id,
                                                      candidateCapabilities);
                  } else {
                    (iter->second).emplace(aIndex, webrtcCaps);
                  }
                }
                if (error) {
                  return Promise::CreateAndReject(
                      error, "CamerasParent::RecvGetCaptureCapability");
                }
                return Promise::CreateAndResolve(
                    webrtcCaps, "CamerasParent::RecvGetCaptureCapability");
              })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            if (mDestroyed) {
              LOG("RecvGetCaptureCapability: child not alive");
              return;
            }

            if (aValue.IsReject()) {
              LOG("RecvGetCaptureCapability: reply failure");
              Unused << SendReplyFailure();
              return;
            }

            auto webrtcCaps = aValue.ResolveValue();
            VideoCaptureCapability capCap(
                webrtcCaps.width, webrtcCaps.height, webrtcCaps.maxFPS,
                static_cast<int>(webrtcCaps.videoType), webrtcCaps.interlaced);
            LOG("Capability: %u %u %u %d %d", webrtcCaps.width,
                webrtcCaps.height, webrtcCaps.maxFPS,
                static_cast<int>(webrtcCaps.videoType), webrtcCaps.interlaced);
            Unused << SendReplyGetCaptureCapability(capCap);
          });
  return IPC_OK();
}

ipc::IPCResult CamerasParent::RecvGetCaptureDevice(
    const CaptureEngine& aCapEngine, const int& aDeviceIndex) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();

  using Data = std::tuple<nsCString, nsCString, pid_t, boolint>;
  using Promise = MozPromise<Data, booltrue>;
  InvokeAsync(mVideoCaptureThread, __func__,
              [this, self = RefPtr(this), aCapEngine, aDeviceIndex] {
                char deviceName[MediaEngineSource::kMaxDeviceNameLength];
                char deviceUniqueId[MediaEngineSource::kMaxUniqueIdLength];
                nsCString name;
                nsCString uniqueId;
                pid_t devicePid = 0;
                bool placeholder = false;
                int error = -1;
                if (auto devInfo = GetDeviceInfo(aCapEngine)) {
                  error = devInfo->GetDeviceName(
                      aDeviceIndex, deviceName, sizeof(deviceName),
                      deviceUniqueId, sizeof(deviceUniqueId), nullptr, 0,
                      &devicePid, &placeholder);
                }

                if (error == 0) {
                  name.Assign(deviceName);
                  uniqueId.Assign(deviceUniqueId);
                }

                return Promise::CreateAndResolve(
                    std::make_tuple(std::move(name), std::move(uniqueId),
                                    devicePid, placeholder, error),
                    "CamerasParent::RecvGetCaptureDevice");
              })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            const auto& [name, uniqueId, devicePid, placeholder, error] =
                aValue.ResolveValue();
            if (mDestroyed) {
              return;
            }
            if (error != 0) {
              LOG("GetCaptureDevice failed: %d", error);
              Unused << SendReplyFailure();
              return;
            }
            bool scary = (devicePid == getpid());

            LOG("Returning %s name %s id (pid = %d)%s", name.get(),
                uniqueId.get(), devicePid, (scary ? " (scary)" : ""));
            Unused << SendReplyGetCaptureDevice(name, uniqueId, scary,
                                                placeholder);
          });
  return IPC_OK();
}

// Find out whether the given window with id has permission to use the
// camera. If the permission is not persistent, we'll make it a one-shot by
// removing the (session) permission.
static bool HasCameraPermission(const uint64_t& aWindowId) {
  MOZ_ASSERT(NS_IsMainThread());

  RefPtr<dom::WindowGlobalParent> window =
      dom::WindowGlobalParent::GetByInnerWindowId(aWindowId);
  if (!window) {
    // Could not find window by id
    return false;
  }

  // when we delegate permission from first party, we should use the top level
  // window
  RefPtr<dom::BrowsingContext> topBC = window->BrowsingContext()->Top();
  window = topBC->Canonical()->GetCurrentWindowGlobal();

  // Return false if the window is not the currently-active window for its
  // BrowsingContext.
  if (!window || !window->IsCurrentGlobal()) {
    return false;
  }

  nsIPrincipal* principal = window->DocumentPrincipal();
  if (principal->GetIsNullPrincipal()) {
    return false;
  }

  if (principal->IsSystemPrincipal()) {
    return true;
  }

  MOZ_ASSERT(principal->GetIsContentPrincipal());

  nsresult rv;
  // Name used with nsIPermissionManager
  static const nsLiteralCString cameraPermission = "MediaManagerVideo"_ns;
  nsCOMPtr<nsIPermissionManager> mgr =
      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
  rv = mgr->TestExactPermissionFromPrincipal(principal, cameraPermission,
                                             &video);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  bool allowed = (video == nsIPermissionManager::ALLOW_ACTION);

  // Session permissions are removed after one use.
  if (allowed) {
    mgr->RemoveFromPrincipal(principal, cameraPermission);
  }

  return allowed;
}

ipc::IPCResult CamerasParent::RecvAllocateCapture(
    const CaptureEngine& aCapEngine, const nsACString& aUniqueIdUTF8,
    const uint64_t& aWindowID) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG("CamerasParent(%p)::%s: Verifying permissions"this, __func__);

  using Promise1 = MozPromise<boolbooltrue>;
  using Data = std::tuple<intint>;
  using Promise2 = MozPromise<Data, booltrue>;
  InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
              [aWindowID] {
                // Verify whether the claimed origin has received permission
                // to use the camera, either persistently or this session (one
                // shot).
                bool allowed = HasCameraPermission(aWindowID);
                if (!allowed) {
                  // Developer preference for turning off permission check.
                  if (Preferences::GetBool(
                          "media.navigator.permission.disabled"false)) {
                    allowed = true;
                    LOG("No permission but checks are disabled");
                  } else {
                    LOG("No camera permission for this origin");
                  }
                }
                return Promise1::CreateAndResolve(
                    allowed, "CamerasParent::RecvAllocateCapture");
              })
      ->Then(mVideoCaptureThread, __func__,
             [this, self = RefPtr(this), aCapEngine,
              unique_id = nsCString(aUniqueIdUTF8)](
                 Promise1::ResolveOrRejectValue&& aValue) {
               bool allowed = aValue.ResolveValue();
               int captureId = -1;
               int error = -1;
               if (allowed && EnsureInitialized(aCapEngine)) {
                 VideoEngine* engine = mEngines->ElementAt(aCapEngine);
                 captureId = engine->CreateVideoCapture(unique_id.get());
                 engine->WithEntry(captureId,
                                   [&error](VideoEngine::CaptureEntry& cap) {
                                     if (cap.VideoCapture()) {
                                       error = 0;
                                     }
                                   });
               }
               return Promise2::CreateAndResolve(
                   std::make_tuple(captureId, error),
                   "CamerasParent::RecvAllocateCapture");
             })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise2::ResolveOrRejectValue&& aValue) {
            const auto [captureId, error] = aValue.ResolveValue();
            if (mDestroyed) {
              LOG("RecvAllocateCapture: child not alive");
              return;
            }

            if (error != 0) {
              Unused << SendReplyFailure();
              LOG("RecvAllocateCapture: WithEntry error");
              return;
            }

            LOG("Allocated device nr %d", captureId);
            Unused << SendReplyAllocateCapture(captureId);
          });
  return IPC_OK();
}

int CamerasParent::ReleaseCapture(const CaptureEngine& aCapEngine,
                                  int aCaptureId) {
  MOZ_ASSERT(mVideoCaptureThread->IsOnCurrentThread());
  int error = -1;
  if (auto* engine = EnsureInitialized(aCapEngine)) {
    error = engine->ReleaseVideoCapture(aCaptureId);
  }
  return error;
}

ipc::IPCResult CamerasParent::RecvReleaseCapture(
    const CaptureEngine& aCapEngine, const int& aCaptureId) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();
  LOG("RecvReleaseCamera device nr %d", aCaptureId);

  using Promise = MozPromise<intbooltrue>;
  InvokeAsync(mVideoCaptureThread, __func__,
              [this, self = RefPtr(this), aCapEngine, aCaptureId] {
                return Promise::CreateAndResolve(
                    ReleaseCapture(aCapEngine, aCaptureId),
                    "CamerasParent::RecvReleaseCapture");
              })
      ->Then(mPBackgroundEventTarget, __func__,
             [this, self = RefPtr(this),
              aCaptureId](Promise::ResolveOrRejectValue&& aValue) {
               int error = aValue.ResolveValue();

               if (mDestroyed) {
                 LOG("RecvReleaseCapture: child not alive");
                 return;
               }

               if (error != 0) {
                 Unused << SendReplyFailure();
                 LOG("RecvReleaseCapture: Failed to free device nr %d",
                     aCaptureId);
                 return;
               }

               Unused << SendReplySuccess();
               LOG("Freed device nr %d", aCaptureId);
             });
  return IPC_OK();
}

ipc::IPCResult CamerasParent::RecvStartCapture(
    const CaptureEngine& aCapEngine, const int& aCaptureId,
    const VideoCaptureCapability& aIpcCaps) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();

  using Promise = MozPromise<intbooltrue>;
  InvokeAsync(
      mVideoCaptureThread, __func__,
      [this, self = RefPtr(this), aCapEngine, aCaptureId, aIpcCaps] {
        LOG_FUNCTION();
        int error = -1;

        if (!EnsureInitialized(aCapEngine)) {
          return Promise::CreateAndResolve(error,
                                           "CamerasParent::RecvStartCapture");
        }

        mEngines->ElementAt(aCapEngine)
            ->WithEntry(aCaptureId, [&](VideoEngine::CaptureEntry& cap) {
              webrtc::VideoCaptureCapability capability;
              capability.width = aIpcCaps.width();
              capability.height = aIpcCaps.height();
              capability.maxFPS = aIpcCaps.maxFPS();
              capability.videoType =
                  static_cast<webrtc::VideoType>(aIpcCaps.videoType());
              capability.interlaced = aIpcCaps.interlaced();

              if (sDeviceUniqueIDs.find(aCaptureId) == sDeviceUniqueIDs.end()) {
                sDeviceUniqueIDs.emplace(
                    aCaptureId, cap.VideoCapture()->CurrentDeviceName());
                sAllRequestedCapabilities.emplace(aCaptureId, capability);
              } else {
                // Starting capture for an id that already exists. Update its
                // requested capability.
                MOZ_DIAGNOSTIC_ASSERT(
                    strcmp(sDeviceUniqueIDs[aCaptureId],
                           cap.VideoCapture()->CurrentDeviceName()) == 0);
                MOZ_DIAGNOSTIC_ASSERT(
                    sAllRequestedCapabilities.find(aCaptureId) !=
                    sAllRequestedCapabilities.end());
                sAllRequestedCapabilities[aCaptureId] = capability;
              }

              if (aCapEngine == CameraEngine) {
                for (const auto& it : sDeviceUniqueIDs) {
                  if (strcmp(it.second,
                             cap.VideoCapture()->CurrentDeviceName()) == 0) {
                    capability.width =
                        std::max(capability.width,
                                 sAllRequestedCapabilities[it.first].width);
                    capability.height =
                        std::max(capability.height,
                                 sAllRequestedCapabilities[it.first].height);
                    capability.maxFPS =
                        std::max(capability.maxFPS,
                                 sAllRequestedCapabilities[it.first].maxFPS);
                  }
                }

                auto candidateCapabilities = mAllCandidateCapabilities.find(
                    nsCString(cap.VideoCapture()->CurrentDeviceName()));
                if ((candidateCapabilities !=
                     mAllCandidateCapabilities.end()) &&
                    (!candidateCapabilities->second.empty())) {
                  int32_t minIdx = -1;
                  uint64_t minDistance = UINT64_MAX;

                  for (auto& candidateCapability :
                       candidateCapabilities->second) {
                    if (candidateCapability.second.videoType !=
                        capability.videoType) {
                      continue;
                    }
                    // The first priority is finding a suitable resolution.
                    // So here we raise the weight of width and height
                    uint64_t distance = uint64_t(ResolutionFeasibilityDistance(
                                            candidateCapability.second.width,
                                            capability.width)) +
                                        uint64_t(ResolutionFeasibilityDistance(
                                            candidateCapability.second.height,
                                            capability.height)) +
                                        uint64_t(FeasibilityDistance(
                                            candidateCapability.second.maxFPS,
                                            capability.maxFPS));
                    if (distance < minDistance) {
                      minIdx = static_cast<int32_t>(candidateCapability.first);
                      minDistance = distance;
                    }
                  }
                  MOZ_ASSERT(minIdx != -1);
                  capability = candidateCapabilities->second[minIdx];
                }
              } else if (aCapEngine == ScreenEngine ||
                         aCapEngine == BrowserEngine ||
                         aCapEngine == WinEngine) {
                for (const auto& it : sDeviceUniqueIDs) {
                  if (strcmp(it.second,
                             cap.VideoCapture()->CurrentDeviceName()) == 0) {
                    capability.maxFPS =
                        std::max(capability.maxFPS,
                                 sAllRequestedCapabilities[it.first].maxFPS);
                  }
                }
              }

              bool cbhExists = false;
              CallbackHelper** cbh = nullptr;
              for (auto* cb : mCallbacks) {
                if (cb->mCapEngine == aCapEngine &&
                    cb->mStreamId == (uint32_t)aCaptureId) {
                  cbhExists = true;
                  break;
                }
              }
              if (!cbhExists) {
                cbh = mCallbacks.AppendElement(new CallbackHelper(
                    static_cast<CaptureEngine>(aCapEngine), aCaptureId, this));
                cap.VideoCapture()->SetTrackingId(
                    (*cbh)->mTrackingId.mUniqueInProcId);
              }

              error = cap.VideoCapture()->StartCapture(capability);

              if (!error) {
                if (cbh) {
                  cap.VideoCapture()->RegisterCaptureDataCallback(
                      static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(
                          *cbh));
                  if (auto* event = cap.CaptureEndedEvent();
                      event && !(*cbh)->mConnectedToCaptureEnded) {
                    (*cbh)->mCaptureEndedListener =
                        event->Connect(mVideoCaptureThread, (*cbh),
                                       &CallbackHelper::OnCaptureEnded);
                    (*cbh)->mConnectedToCaptureEnded = true;
                  }
                }
              } else {
                sDeviceUniqueIDs.erase(aCaptureId);
                sAllRequestedCapabilities.erase(aCaptureId);
              }
            });

        return Promise::CreateAndResolve(error,
                                         "CamerasParent::RecvStartCapture");
      })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            int error = aValue.ResolveValue();

            if (mDestroyed) {
              LOG("RecvStartCapture failure: child is not alive");
              return;
            }

            if (error != 0) {
              LOG("RecvStartCapture failure: StartCapture failed");
              Unused << SendReplyFailure();
              return;
            }

            Unused << SendReplySuccess();
          });
  return IPC_OK();
}

ipc::IPCResult CamerasParent::RecvFocusOnSelectedSource(
    const CaptureEngine& aCapEngine, const int& aCaptureId) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();

  using Promise = MozPromise<boolbooltrue>;
  InvokeAsync(mVideoCaptureThread, __func__,
              [this, self = RefPtr(this), aCapEngine, aCaptureId] {
                bool result = false;
                if (auto* engine = EnsureInitialized(aCapEngine)) {
                  engine->WithEntry(
                      aCaptureId, [&](VideoEngine::CaptureEntry& cap) {
                        if (cap.VideoCapture()) {
                          result = cap.VideoCapture()->FocusOnSelectedSource();
                        }
                      });
                }
                return Promise::CreateAndResolve(
                    result, "CamerasParent::RecvFocusOnSelectedSource");
              })
      ->Then(
          mPBackgroundEventTarget, __func__,
          [this, self = RefPtr(this)](Promise::ResolveOrRejectValue&& aValue) {
            bool result = aValue.ResolveValue();
            if (mDestroyed) {
              LOG("RecvFocusOnSelectedSource failure: child is not alive");
              return;
            }

            if (!result) {
              Unused << SendReplyFailure();
              LOG("RecvFocusOnSelectedSource failure.");
              return;
            }

            Unused << SendReplySuccess();
          });
  return IPC_OK();
}

void CamerasParent::StopCapture(const CaptureEngine& aCapEngine,
                                int aCaptureId) {
  MOZ_ASSERT(mVideoCaptureThread->IsOnCurrentThread());
  if (auto* engine = EnsureInitialized(aCapEngine)) {
    // we're removing elements, iterate backwards
    for (size_t i = mCallbacks.Length(); i > 0; i--) {
      if (mCallbacks[i - 1]->mCapEngine == aCapEngine &&
          mCallbacks[i - 1]->mStreamId == (uint32_t)aCaptureId) {
        CallbackHelper* cbh = mCallbacks[i - 1];
        engine->WithEntry(aCaptureId, [cbh, &aCaptureId](
                                          VideoEngine::CaptureEntry& cap) {
          if (cap.VideoCapture()) {
            cap.VideoCapture()->DeRegisterCaptureDataCallback(
                static_cast<rtc::VideoSinkInterface<webrtc::VideoFrame>*>(cbh));
            cap.VideoCapture()->StopCaptureIfAllClientsClose();

            sDeviceUniqueIDs.erase(aCaptureId);
            sAllRequestedCapabilities.erase(aCaptureId);
          }
        });
        cbh->mCaptureEndedListener.DisconnectIfExists();
        delete mCallbacks[i - 1];
        mCallbacks.RemoveElementAt(i - 1);
        break;
      }
    }
  }
}

ipc::IPCResult CamerasParent::RecvStopCapture(const CaptureEngine& aCapEngine,
                                              const int& aCaptureId) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  MOZ_ASSERT(!mDestroyed);

  LOG_FUNCTION();

  nsresult rv = mVideoCaptureThread->Dispatch(NS_NewRunnableFunction(
      __func__, [this, self = RefPtr(this), aCapEngine, aCaptureId] {
        StopCapture(aCapEngine, aCaptureId);
      }));

  if (mDestroyed) {
    if (NS_FAILED(rv)) {
      return IPC_FAIL_NO_REASON(this);
    }
  } else {
    if (NS_SUCCEEDED(rv)) {
      if (!SendReplySuccess()) {
        return IPC_FAIL_NO_REASON(this);
      }
    } else {
      if (!SendReplyFailure()) {
        return IPC_FAIL_NO_REASON(this);
      }
    }
  }
  return IPC_OK();
}

void CamerasParent::ActorDestroy(ActorDestroyReason aWhy) {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());
  LOG_FUNCTION();

  // Release shared memory now, it's our last chance
  mShmemPool.Cleanup(this);
  // We don't want to receive callbacks or anything if we can't
  // forward them anymore anyway.
  mDestroyed = true;
  // We don't need to listen for shutdown any longer. Disconnect the request.
  // This breaks the reference cycle between CamerasParent and the shutdown
  // promise's Then handler.
  mShutdownRequest.DisconnectIfExists();

  if (mVideoCaptureThread) {
    // Shut down the WebRTC stack, on the video capture thread.
    MOZ_ALWAYS_SUCCEEDS(mVideoCaptureThread->Dispatch(
        NewRunnableMethod(__func__, this, &CamerasParent::CloseEngines)));
  }
}

void CamerasParent::OnShutdown() {
  ipc::AssertIsOnBackgroundThread();
  LOG("CamerasParent(%p) ShutdownEvent"this);
  mShutdownRequest.Complete();
  (void)Send__delete__(this);
}

CamerasParent::CamerasParent()
    : mShutdownBlocker(ShutdownBlockingTicket::Create(
          u"CamerasParent"_ns, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
          __LINE__)),
      mVideoCaptureThread(mShutdownBlocker
                              ? MakeAndAddRefVideoCaptureThreadAndSingletons()
                              : nullptr),
      mEngines(sEngines),
      mVideoCaptureFactory(EnsureVideoCaptureFactory()),
      mShmemPool(CaptureEngine::MaxEngine),
      mPBackgroundEventTarget(GetCurrentSerialEventTarget()),
      mDestroyed(false) {
  MOZ_ASSERT(mPBackgroundEventTarget != nullptr,
             "GetCurrentThreadEventTarget failed");
  LOG("CamerasParent: %p"this);

  // Don't dispatch from the constructor a runnable that may toggle the
  // reference count, because the IPC thread does not get a reference until
  // after the constructor returns.
}

/* static */
auto CamerasParent::RequestCameraAccess(bool aAllowPermissionRequest)
    -> RefPtr<CameraAccessRequestPromise> {
  ipc::AssertIsOnBackgroundThread();

  // Special case for PipeWire where we at this point just need to make sure
  // we have information about camera availabilty through the camera portal
  if (!aAllowPermissionRequest) {
    return EnsureVideoCaptureFactory()->UpdateCameraAvailability()->Then(
        GetCurrentSerialEventTarget(),
        "CamerasParent::RequestCameraAccess update camera availability",
        [](const VideoCaptureFactory::UpdateCameraAvailabilityPromise::
               ResolveOrRejectValue& aValue) {
          LOG("Camera availability updated to %s",
              aValue.IsResolve()
                  ? aValue.ResolveValue() ==
                            VideoCaptureFactory::CameraAvailability::Available
                        ? "available"
                        : "not available"
                  : "still unknown");
          return CameraAccessRequestPromise::CreateAndResolve(
              CamerasAccessStatus::RequestRequired,
              "CamerasParent::RequestCameraAccess camera availability updated");
        });
  }

  static StaticRefPtr<CameraAccessRequestPromise> sCameraAccessRequestPromise;
  if (!sCameraAccessRequestPromise) {
    sCameraAccessRequestPromise = RefPtr<CameraAccessRequestPromise>(
        EnsureVideoCaptureFactory()->InitCameraBackend()->Then(
            GetCurrentSerialEventTarget(),
            "CamerasParent::RequestCameraAccess camera backend init handler",
            [](nsresult aRv) mutable {
              MOZ_ASSERT(NS_SUCCEEDED(aRv));
              if (sVideoCaptureThread) {
                MOZ_ASSERT(sEngines);
                MOZ_ALWAYS_SUCCEEDS(
                    sVideoCaptureThread->Dispatch(NS_NewRunnableFunction(
                        __func__, [engines = RefPtr(sEngines.get())] {
                          if (VideoEngine* engine =
                                  engines->ElementAt(CameraEngine)) {
                            engine->ClearVideoCaptureDeviceInfo();
                          }
                        })));
              }
              return CameraAccessRequestPromise::CreateAndResolve(
                  CamerasAccessStatus::Granted,
                  "CamerasParent::RequestCameraAccess camera backend init "
                  "resolve");
            },
            [](nsresult aRv) mutable {
              MOZ_ASSERT(NS_FAILED(aRv));
              return CameraAccessRequestPromise::CreateAndResolve(
                  aRv == NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR
                      ? CamerasAccessStatus::Rejected
                      : CamerasAccessStatus::Error,
                  "CamerasParent::RequestCameraAccess camera backend init "
                  "reject");
            }));
    static nsresult clearingRv = NS_DispatchToMainThread(NS_NewRunnableFunction(
        __func__, [] { ClearOnShutdown(&sCameraAccessRequestPromise); }));
    Unused << clearingRv;
  }

  // If camera acess is granted, all is jolly. But we need to handle rejection.
  return sCameraAccessRequestPromise->Then(
      GetCurrentSerialEventTarget(),
      "CamerasParent::CameraAccessRequestPromise rejection handler",
      [](CamerasAccessStatus aStatus) {
        return CameraAccessRequestPromise::CreateAndResolve(
            aStatus, "CamerasParent::RequestCameraAccess resolve");
      },
      [promise = RefPtr(sCameraAccessRequestPromise.get()),
       aAllowPermissionRequest](void_t aRv) {
        if (promise == sCameraAccessRequestPromise) {
          sCameraAccessRequestPromise = nullptr;
          return CameraAccessRequestPromise::CreateAndResolve(
              CamerasAccessStatus::Error,
              "CamerasParent::RequestCameraAccess reject");
        }
        return CamerasParent::RequestCameraAccess(aAllowPermissionRequest);
      });
}

// RecvPCamerasConstructor() is used because IPC messages, for
// Send__delete__(), cannot be sent from AllocPCamerasParent().
ipc::IPCResult CamerasParent::RecvPCamerasConstructor() {
  MOZ_ASSERT(mPBackgroundEventTarget->IsOnCurrentThread());

  // AsyncShutdown barriers are available only for ShutdownPhases as late as
  // XPCOMWillShutdown.  The IPC background thread shuts down during
  // XPCOMShutdownThreads, so actors may be created when AsyncShutdown barriers
  // are no longer available.  Should shutdown be past XPCOMWillShutdown we end
  // up with a null mShutdownBlocker.

  if (!mShutdownBlocker) {
    LOG("CamerasParent(%p) Got no ShutdownBlockingTicket. We are already in "
        "shutdown. Deleting.",
        this);
    return Send__delete__(this) ? IPC_OK() : IPC_FAIL(this"Failed to send");
  }

  if (!mVideoCaptureThread) {
    return Send__delete__(this) ? IPC_OK() : IPC_FAIL(this"Failed to send");
  }

  NS_DispatchToMainThread(
      NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)] {
        mLogHandle = new nsMainThreadPtrHolder<WebrtcLogSinkHandle>(
            "CamerasParent::mLogHandle", EnsureWebrtcLogging());
      }));

  MOZ_ASSERT(mEngines);

  mShutdownBlocker->ShutdownPromise()
      ->Then(mPBackgroundEventTarget, "CamerasParent OnShutdown",
             [this, self = RefPtr(this)](
                 const ShutdownPromise::ResolveOrRejectValue& aValue) {
               MOZ_ASSERT(aValue.IsResolve(),
                          "ShutdownBlockingTicket must have been destroyed "
                          "without us disconnecting the shutdown request");
               OnShutdown();
             })
      ->Track(mShutdownRequest);

  return IPC_OK();
}

CamerasParent::~CamerasParent() {
  ipc::AssertIsOnBackgroundThread();
  LOG_FUNCTION();

  if (!mVideoCaptureThread) {
    // No video engines or video capture thread to shutdown here.
    return;
  }

  MOZ_ASSERT(mShutdownBlocker,
             "A ShutdownBlocker is a prerequisite for mVideoCaptureThread");

  ReleaseVideoCaptureThreadAndSingletons();
}

already_AddRefed<CamerasParent> CamerasParent::Create() {
  ipc::AssertIsOnBackgroundThread();
  return MakeAndAddRef<CamerasParent>();
}

}  // namespace camera
}  // namespace mozilla

Messung V0.5
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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.