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 19 kB image not shown  

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

#undef FF

#include "mozilla/Assertions.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/Logging.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/WeakPtr.h"
#include "mozilla/Unused.h"
#include "MediaUtils.h"
#include "nsThreadUtils.h"

#undef LOG
#undef LOG_ENABLED
mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
#define LOG(args) MOZ_LOG(gCamerasChildLog, mozilla::LogLevel::Debug, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasChildLog, mozilla::LogLevel::Debug)

namespace mozilla::camera {

CamerasSingleton::CamerasSingleton()
    : mCamerasMutex("CamerasSingleton::mCamerasMutex"),
      mCameras(nullptr),
      mCamerasChildThread(nullptr) {
  LOG(("CamerasSingleton: %p"this));
}

CamerasSingleton::~CamerasSingleton() { LOG(("~CamerasSingleton: %p"this)); }

class InitializeIPCThread : public Runnable {
 public:
  InitializeIPCThread()
      : Runnable("camera::InitializeIPCThread"), mCamerasChild(nullptr) {}

  NS_IMETHOD Run() override {
    // Get the PBackground handle
    ipc::PBackgroundChild* existingBackgroundChild =
        ipc::BackgroundChild::GetOrCreateForCurrentThread();
    LOG(("BackgroundChild: %p", existingBackgroundChild));
    if (!existingBackgroundChild) {
      return NS_ERROR_FAILURE;
    }

    // Create CamerasChild
    // We will be returning the resulting pointer (synchronously) to our caller.
    mCamerasChild = static_cast<mozilla::camera::CamerasChild*>(
        existingBackgroundChild->SendPCamerasConstructor());
    if (!mCamerasChild) {
      return NS_ERROR_FAILURE;
    }
    return NS_OK;
  }

  CamerasChild* GetCamerasChild() { return mCamerasChild; }

 private:
  CamerasChild* mCamerasChild;
};

CamerasChild* GetCamerasChild() {
  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
  if (!CamerasSingleton::Child()) {
    MOZ_ASSERT(!NS_IsMainThread(), "Should not be on the main Thread");
    MOZ_ASSERT(!CamerasSingleton::Thread());
    LOG(("No sCameras, setting up IPC Thread"));
    nsresult rv = NS_NewNamedThread("Cameras IPC",
                                    getter_AddRefs(CamerasSingleton::Thread()));
    if (NS_FAILED(rv)) {
      LOG(("Error launching IPC Thread"));
      return nullptr;
    }

    // At this point we are in the MediaManager thread, and the thread we are
    // dispatching to is the specific Cameras IPC thread that was just made
    // above, so now we will fire off a runnable to run
    // BackgroundChild::GetOrCreateForCurrentThread there, while we
    // block in this thread.
    // We block until the following happens in the Cameras IPC thread:
    // 1) Creation of PBackground finishes
    // 2) Creation of PCameras finishes by sending a message to the parent
    RefPtr<InitializeIPCThread> runnable = new InitializeIPCThread();
    RefPtr<SyncRunnable> sr = new SyncRunnable(runnable);
    sr->DispatchToThread(CamerasSingleton::Thread());
    CamerasSingleton::Child() = runnable->GetCamerasChild();
  }
  if (!CamerasSingleton::Child()) {
    LOG(("Failed to set up CamerasChild, are we in shutdown?"));
  }
  return CamerasSingleton::Child();
}

CamerasChild* GetCamerasChildIfExists() {
  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
  return CamerasSingleton::Child();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplyFailure(void) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = false;
  monitor.Notify();
  return IPC_OK();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplySuccess(void) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = true;
  monitor.Notify();
  return IPC_OK();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplyNumberOfCapabilities(
    const int& capabilityCount) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = true;
  mReplyInteger = capabilityCount;
  monitor.Notify();
  return IPC_OK();
}

// Helper function to dispatch calls to the IPC Thread and
// CamerasChild object. Takes the needed locks and dispatches.
// Takes a "failed" value and a reference to the output variable
// as parameters, will return the right one depending on whether
// dispatching succeeded.
//
// The LockAndDispatch object in the caller must stay alive until after any
// reply data has been retreived (mReplyInteger, etc) so that the data is
// protected by the ReplyMonitor/RequestMutex
template <class T = int>
class LockAndDispatch {
 public:
  LockAndDispatch(CamerasChild* aCamerasChild, const char* aRequestingFunc,
                  nsIRunnable* aRunnable, T aFailureValue,
                  const T& aSuccessValue)
      : mCamerasChild(aCamerasChild),
        mRequestingFunc(aRequestingFunc),
        mRunnable(aRunnable),
        mReplyLock(aCamerasChild->mReplyMonitor),
        mRequestLock(aCamerasChild->mRequestMutex),
        mSuccess(true),
        mFailureValue(aFailureValue),
        mSuccessValue(aSuccessValue) {
    Dispatch();
  }

  T ReturnValue() const {
    if (mSuccess) {
      return mSuccessValue;
    } else {
      return mFailureValue;
    }
  }

  const bool& Success() const { return mSuccess; }

 private:
  void Dispatch() {
    if (!mCamerasChild->DispatchToParent(mRunnable, mReplyLock)) {
      LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc));
      mSuccess = false;
    }
  }

  CamerasChild* mCamerasChild;
  const char* mRequestingFunc;
  nsIRunnable* mRunnable;
  // Prevent concurrent use of the reply variables by holding
  // the mReplyMonitor. Note that this is unlocked while waiting for
  // the reply to be filled in, necessitating the additional mRequestLock/Mutex;
  MonitorAutoLock mReplyLock;
  MutexAutoLock mRequestLock;
  bool mSuccess;
  const T mFailureValue;
  const T& mSuccessValue;
};

bool CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
                                    MonitorAutoLock& aMonitor) {
  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
  mReplyMonitor.AssertCurrentThreadOwns();
  CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
  // Guard against spurious wakeups.
  mReceivedReply = false;
  // Wait for a reply
  do {
    // If the parent has been shut down, then we won't receive a reply.
    if (!mIPCIsAlive) {
      return false;
    }
    aMonitor.Wait();
  } while (!mReceivedReply);
  return mReplySuccess;
}

int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
                                       const char* deviceUniqueIdUTF8) {
  LOG(("%s", __PRETTY_FUNCTION__));
  LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8));
  nsCString unique_id(deviceUniqueIdUTF8);
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, nsCString>(
          "camera::PCamerasChild::SendNumberOfCapabilities"this,
          &CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id);
  LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
  LOG(("Capture capability count: %d", dispatcher.ReturnValue()));
  return dispatcher.ReturnValue();
}

int CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>(
      "camera::PCamerasChild::SendNumberOfCaptureDevices"this,
      &CamerasChild::SendNumberOfCaptureDevices, aCapEngine);
  LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
  LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
  return dispatcher.ReturnValue();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplyNumberOfCaptureDevices(
    const int& aDeviceCount) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = true;
  mReplyInteger = aDeviceCount;
  monitor.Notify();
  return IPC_OK();
}

int CamerasChild::EnsureInitialized(CaptureEngine aCapEngine) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>(
      "camera::PCamerasChild::SendEnsureInitialized"this,
      &CamerasChild::SendEnsureInitialized, aCapEngine);
  LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
  LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
  return dispatcher.ReturnValue();
}

int CamerasChild::GetCaptureCapability(
    CaptureEngine aCapEngine, const char* unique_idUTF8,
    const unsigned int capability_number,
    webrtc::VideoCaptureCapability* capability) {
  LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number));
  MOZ_ASSERT(capability);
  nsCString unique_id(unique_idUTF8);
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, nsCString, unsigned int>(
          "camera::PCamerasChild::SendGetCaptureCapability"this,
          &CamerasChild::SendGetCaptureCapability, aCapEngine, unique_id,
          capability_number);
  mReplyCapability = capability;
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
  mReplyCapability = nullptr;
  return dispatcher.ReturnValue();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplyGetCaptureCapability(
    const VideoCaptureCapability& ipcCapability) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = true;
  mReplyCapability->width = ipcCapability.width();
  mReplyCapability->height = ipcCapability.height();
  mReplyCapability->maxFPS = ipcCapability.maxFPS();
  mReplyCapability->videoType =
      static_cast<webrtc::VideoType>(ipcCapability.videoType());
  mReplyCapability->interlaced = ipcCapability.interlaced();
  monitor.Notify();
  return IPC_OK();
}

int CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
                                   unsigned int list_number,
                                   char* device_nameUTF8,
                                   const unsigned int device_nameUTF8Length,
                                   char* unique_idUTF8,
                                   const unsigned int unique_idUTF8Length,
                                   bool* scary, bool* device_is_placeholder) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, unsigned int>(
          "camera::PCamerasChild::SendGetCaptureDevice"this,
          &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number);
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
  if (dispatcher.Success()) {
    base::strlcpy(device_nameUTF8, mReplyDeviceName.get(),
                  device_nameUTF8Length);
    base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length);
    if (scary) {
      *scary = mReplyScary;
    }
    if (device_is_placeholder) {
      *device_is_placeholder = mReplyDeviceIsPlaceholder;
    }
    LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8));
  }
  return dispatcher.ReturnValue();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplyGetCaptureDevice(
    const nsACString& device_name, const nsACString& device_id,
    const bool& scary, const bool& device_is_placeholder) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = true;
  mReplyDeviceName = device_name;
  mReplyDeviceID = device_id;
  mReplyScary = scary;
  mReplyDeviceIsPlaceholder = device_is_placeholder;
  monitor.Notify();
  return IPC_OK();
}

int CamerasChild::AllocateCapture(CaptureEngine aCapEngine,
                                  const char* unique_idUTF8,
                                  uint64_t aWindowID) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCString unique_id(unique_idUTF8);
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, nsCString, uint64_t>(
          "camera::PCamerasChild::SendAllocateCapture"this,
          &CamerasChild::SendAllocateCapture, aCapEngine, unique_id, aWindowID);
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mReplyInteger);
  if (dispatcher.Success()) {
    LOG(("Capture Device allocated: %d", mReplyInteger));
  }
  return dispatcher.ReturnValue();
}

mozilla::ipc::IPCResult CamerasChild::RecvReplyAllocateCapture(
    const int& aCaptureId) {
  LOG(("%s", __PRETTY_FUNCTION__));
  MonitorAutoLock monitor(mReplyMonitor);
  mReceivedReply = true;
  mReplySuccess = true;
  mReplyInteger = aCaptureId;
  monitor.Notify();
  return IPC_OK();
}

int CamerasChild::ReleaseCapture(CaptureEngine aCapEngine,
                                 const int capture_id) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, int>(
          "camera::PCamerasChild::SendReleaseCapture"this,
          &CamerasChild::SendReleaseCapture, aCapEngine, capture_id);
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
  return dispatcher.ReturnValue();
}

void CamerasChild::AddCallback(int capture_id, FrameRelay* render) {
  MutexAutoLock lock(mCallbackMutex);
  CapturerElement ce;
  ce.id = capture_id;
  ce.callback = render;

  if (!mCallbacks.Contains(ce, [](const auto& aLhs, const auto& aRhs) -> int {
        if (int res = aLhs.id - aRhs.id; res != 0) {
          return res;
        }
        return aLhs.callback - aRhs.callback;
      })) {
    mCallbacks.AppendElement(ce);
  }
}

void CamerasChild::RemoveCallback(const int capture_id) {
  MutexAutoLock lock(mCallbackMutex);
  for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
    CapturerElement ce = mCallbacks[i];
    if (ce.id == capture_id) {
      mCallbacks.RemoveElementAt(i);
      break;
    }
  }
}

int CamerasChild::StartCapture(CaptureEngine aCapEngine, const int capture_id,
                               const webrtc::VideoCaptureCapability& webrtcCaps,
                               FrameRelay* cb) {
  LOG(("%s", __PRETTY_FUNCTION__));
  AddCallback(capture_id, cb);
  VideoCaptureCapability capCap(
      webrtcCaps.width, webrtcCaps.height, webrtcCaps.maxFPS,
      static_cast<int>(webrtcCaps.videoType), webrtcCaps.interlaced);
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, int, VideoCaptureCapability>(
          "camera::PCamerasChild::SendStartCapture"this,
          &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap);
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
  return dispatcher.ReturnValue();
}

int CamerasChild::FocusOnSelectedSource(CaptureEngine aCapEngine,
                                        const int aCaptureId) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, int>(
          "camera::PCamerasChild::SendFocusOnSelectedSource"this,
          &CamerasChild::SendFocusOnSelectedSource, aCapEngine, aCaptureId);
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
  return dispatcher.ReturnValue();
}

int CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) {
  LOG(("%s", __PRETTY_FUNCTION__));
  nsCOMPtr<nsIRunnable> runnable =
      mozilla::NewRunnableMethod<CaptureEngine, int>(
          "camera::PCamerasChild::SendStopCapture"this,
          &CamerasChild::SendStopCapture, aCapEngine, capture_id);
  LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero);
  if (dispatcher.Success()) {
    RemoveCallback(capture_id);
  }
  return dispatcher.ReturnValue();
}

class ShutdownRunnable : public Runnable {
 public:
  explicit ShutdownRunnable(already_AddRefed<Runnable>&& aReplyEvent)
      : Runnable("camera::ShutdownRunnable"), mReplyEvent(aReplyEvent) {};

  NS_IMETHOD Run() override {
    LOG(("Closing BackgroundChild"));
    // This will also destroy the CamerasChild.
    ipc::BackgroundChild::CloseForCurrentThread();

    NS_DispatchToMainThread(mReplyEvent.forget());

    return NS_OK;
  }

 private:
  RefPtr<Runnable> mReplyEvent;
};

void Shutdown(void) {
  // Called from both MediaEngineWebRTC::Shutdown() on the MediaManager thread
  // and DeallocPCamerasChild() on the dedicated IPC thread.
  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());

  CamerasChild* child = CamerasSingleton::Child();
  if (!child) {
    // We don't want to cause everything to get fired up if we're
    // really already shut down.
    LOG(("Shutdown when already shut down"));
    return;
  }
  if (CamerasSingleton::Thread()) {
    LOG(("PBackground thread exists, dispatching close"));
    // The IPC thread is shut down on the main thread after the
    // BackgroundChild is closed.
    RefPtr<ShutdownRunnable> runnable = new ShutdownRunnable(
        NewRunnableMethod("nsIThread::Shutdown", CamerasSingleton::Thread(),
                          &nsIThread::Shutdown));
    CamerasSingleton::Thread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
  } else {
    LOG(("Shutdown called without PBackground thread"));
  }
  LOG(("Erasing sCameras & thread refs (original thread)"));
  CamerasSingleton::Child() = nullptr;
  CamerasSingleton::Thread() = nullptr;
}

mozilla::ipc::IPCResult CamerasChild::RecvCaptureEnded(const int& capId) {
  MutexAutoLock lock(mCallbackMutex);
  if (Callback(capId)) {
    Callback(capId)->OnCaptureEnded();
  } else {
    LOG(("CaptureEnded called with dead callback"));
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult CamerasChild::RecvDeliverFrame(
    const int& capId, mozilla::ipc::Shmem&& shmem,
    const VideoFrameProperties& prop) {
  MutexAutoLock lock(mCallbackMutex);
  if (Callback(capId)) {
    unsigned char* image = shmem.get<unsigned char>();
    Callback(capId)->DeliverFrame(image, prop);
  } else {
    LOG(("DeliverFrame called with dead callback"));
  }
  SendReleaseFrame(std::move(shmem));
  return IPC_OK();
}

mozilla::ipc::IPCResult CamerasChild::RecvDeviceChange() {
  mDeviceListChangeEvent.Notify();
  return IPC_OK();
}

void CamerasChild::ActorDestroy(ActorDestroyReason aWhy) {
  LOG(("ActorDestroy"));
  MonitorAutoLock monitor(mReplyMonitor);
  mIPCIsAlive = false;
  // Hopefully prevent us from getting stuck
  // on replies that'll never come.
  monitor.NotifyAll();
}

CamerasChild::CamerasChild()
    : mCallbackMutex("mozilla::cameras::CamerasChild::mCallbackMutex"),
      mIPCIsAlive(true),
      mRequestMutex("mozilla::cameras::CamerasChild::mRequestMutex"),
      mReplyMonitor("mozilla::cameras::CamerasChild::mReplyMonitor"),
      mReceivedReply(false),
      mReplySuccess(false),
      mZero(0),
      mReplyInteger(0),
      mReplyScary(false) {
  LOG(("CamerasChild: %p"this));

  MOZ_COUNT_CTOR(CamerasChild);
}

CamerasChild::~CamerasChild() {
  LOG(("~CamerasChild: %p"this));
  CamerasSingleton::AssertNoChild();
  MOZ_COUNT_DTOR(CamerasChild);
}

FrameRelay* CamerasChild::Callback(int capture_id) {
  for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
    CapturerElement ce = mCallbacks[i];
    if (ce.id == capture_id) {
      return ce.callback;
    }
  }

  return nullptr;
}

}  // namespace mozilla::camera

Messung V0.5
C=93 H=97 G=94

¤ Dauer der Verarbeitung: 0.28 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.