tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CamerasChild.cpp (20462B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et ft=cpp : */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "CamerasChild.h"
      8 
      9 #undef FF
     10 
     11 #include "MediaUtils.h"
     12 #include "mozilla/Assertions.h"
     13 #include "mozilla/Logging.h"
     14 #include "mozilla/SyncRunnable.h"
     15 #include "mozilla/ipc/BackgroundChild.h"
     16 #include "mozilla/ipc/PBackgroundChild.h"
     17 #include "nsThreadUtils.h"
     18 
     19 mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
     20 #define LOG(args) MOZ_LOG(gCamerasChildLog, mozilla::LogLevel::Debug, args)
     21 #define LOG_ENABLED() MOZ_LOG_TEST(gCamerasChildLog, mozilla::LogLevel::Debug)
     22 
     23 namespace mozilla::camera {
     24 
     25 CamerasSingleton::CamerasSingleton()
     26    : mCamerasMutex("CamerasSingleton::mCamerasMutex"),
     27      mCameras(nullptr),
     28      mCamerasChildThread(nullptr) {
     29  LOG(("CamerasSingleton: %p", this));
     30 }
     31 
     32 CamerasSingleton::~CamerasSingleton() { LOG(("~CamerasSingleton: %p", this)); }
     33 
     34 class InitializeIPCThread : public Runnable {
     35 public:
     36  InitializeIPCThread()
     37      : Runnable("camera::InitializeIPCThread"), mCamerasChild(nullptr) {}
     38 
     39  NS_IMETHOD Run() override {
     40    // Get the PBackground handle
     41    ipc::PBackgroundChild* existingBackgroundChild =
     42        ipc::BackgroundChild::GetOrCreateForCurrentThread();
     43    LOG(("BackgroundChild: %p", existingBackgroundChild));
     44    if (!existingBackgroundChild) {
     45      return NS_ERROR_FAILURE;
     46    }
     47 
     48    // Create CamerasChild
     49    // We will be returning the resulting pointer (synchronously) to our caller.
     50    mCamerasChild = static_cast<mozilla::camera::CamerasChild*>(
     51        existingBackgroundChild->SendPCamerasConstructor());
     52    if (!mCamerasChild) {
     53      return NS_ERROR_FAILURE;
     54    }
     55    return NS_OK;
     56  }
     57 
     58  CamerasChild* GetCamerasChild() { return mCamerasChild; }
     59 
     60 private:
     61  CamerasChild* mCamerasChild;
     62 };
     63 
     64 CamerasChild* GetCamerasChild() {
     65  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
     66  if (!CamerasSingleton::Child()) {
     67    MOZ_ASSERT(!NS_IsMainThread(), "Should not be on the main Thread");
     68    MOZ_ASSERT(!CamerasSingleton::Thread());
     69    LOG(("No sCameras, setting up IPC Thread"));
     70    nsresult rv = NS_NewNamedThread("Cameras IPC",
     71                                    getter_AddRefs(CamerasSingleton::Thread()));
     72    if (NS_FAILED(rv)) {
     73      LOG(("Error launching IPC Thread"));
     74      return nullptr;
     75    }
     76 
     77    // At this point we are in the MediaManager thread, and the thread we are
     78    // dispatching to is the specific Cameras IPC thread that was just made
     79    // above, so now we will fire off a runnable to run
     80    // BackgroundChild::GetOrCreateForCurrentThread there, while we
     81    // block in this thread.
     82    // We block until the following happens in the Cameras IPC thread:
     83    // 1) Creation of PBackground finishes
     84    // 2) Creation of PCameras finishes by sending a message to the parent
     85    RefPtr<InitializeIPCThread> runnable = new InitializeIPCThread();
     86    RefPtr<SyncRunnable> sr = new SyncRunnable(runnable);
     87    sr->DispatchToThread(CamerasSingleton::Thread());
     88    CamerasSingleton::Child() = runnable->GetCamerasChild();
     89  }
     90  if (!CamerasSingleton::Child()) {
     91    LOG(("Failed to set up CamerasChild, are we in shutdown?"));
     92  }
     93  return CamerasSingleton::Child();
     94 }
     95 
     96 CamerasChild* GetCamerasChildIfExists() {
     97  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
     98  return CamerasSingleton::Child();
     99 }
    100 
    101 mozilla::ipc::IPCResult CamerasChild::RecvReplyFailure(void) {
    102  LOG(("%s", __PRETTY_FUNCTION__));
    103  MonitorAutoLock monitor(mReplyMonitor);
    104  mReceivedReply = true;
    105  mReplySuccess = false;
    106  monitor.Notify();
    107  return IPC_OK();
    108 }
    109 
    110 mozilla::ipc::IPCResult CamerasChild::RecvReplySuccess(void) {
    111  LOG(("%s", __PRETTY_FUNCTION__));
    112  MonitorAutoLock monitor(mReplyMonitor);
    113  mReceivedReply = true;
    114  mReplySuccess = true;
    115  monitor.Notify();
    116  return IPC_OK();
    117 }
    118 
    119 mozilla::ipc::IPCResult CamerasChild::RecvReplyNumberOfCapabilities(
    120    const int& capabilityCount) {
    121  LOG(("%s", __PRETTY_FUNCTION__));
    122  MonitorAutoLock monitor(mReplyMonitor);
    123  mReceivedReply = true;
    124  mReplySuccess = true;
    125  mReplyInteger = capabilityCount;
    126  monitor.Notify();
    127  return IPC_OK();
    128 }
    129 
    130 // Helper function to dispatch calls to the IPC Thread and
    131 // CamerasChild object. Takes the needed locks and dispatches.
    132 // Takes a "failed" value and a reference to the output variable
    133 // as parameters, will return the right one depending on whether
    134 // dispatching succeeded.
    135 //
    136 // The LockAndDispatch object in the caller must stay alive until after any
    137 // reply data has been retreived (mReplyInteger, etc) so that the data is
    138 // protected by the ReplyMonitor/RequestMutex
    139 template <class T = int>
    140 class LockAndDispatch {
    141 public:
    142  using Result = CamerasChild::DispatchToParentResult;
    143 
    144  LockAndDispatch(CamerasChild* aCamerasChild, const char* aRequestingFunc,
    145                  nsIRunnable* aRunnable, T aFailureValue, T aIPCFailureValue,
    146                  const T& aSuccessValue)
    147      : mCamerasChild(aCamerasChild),
    148        mRequestingFunc(aRequestingFunc),
    149        mRunnable(aRunnable),
    150        mReplyLock(aCamerasChild->mReplyMonitor),
    151        mRequestLock(aCamerasChild->mRequestMutex),
    152        mStatus(Result::SUCCESS),
    153        mFailureValue(aFailureValue),
    154        mIPCFailureValue(aIPCFailureValue),
    155        mSuccessValue(aSuccessValue) {
    156    Dispatch();
    157  }
    158 
    159  T ReturnValue() const {
    160    if (mStatus == Result::SUCCESS) {
    161      return mSuccessValue;
    162    }
    163    if (mStatus == Result::FAILURE) {
    164      return mFailureValue;
    165    }
    166    MOZ_ASSERT(mStatus == Result::DISCONNECTED);
    167    return mIPCFailureValue;
    168  }
    169 
    170  bool Success() const { return mStatus == Result::SUCCESS; }
    171  bool Disconnected() const { return mStatus == Result::DISCONNECTED; }
    172 
    173 private:
    174  void Dispatch() {
    175    mStatus = mCamerasChild->DispatchToParent(mRunnable, mReplyLock);
    176    if (mStatus != Result::SUCCESS) {
    177      LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc));
    178    }
    179  }
    180 
    181  CamerasChild* mCamerasChild;
    182  const char* mRequestingFunc;
    183  nsIRunnable* mRunnable;
    184  // Prevent concurrent use of the reply variables by holding
    185  // the mReplyMonitor. Note that this is unlocked while waiting for
    186  // the reply to be filled in, necessitating the additional mRequestLock/Mutex;
    187  MonitorAutoLock mReplyLock;
    188  MutexAutoLock mRequestLock;
    189  CamerasChild::DispatchToParentResult mStatus;
    190  const T mFailureValue;
    191  const T mIPCFailureValue;
    192  const T& mSuccessValue;
    193 };
    194 
    195 auto CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
    196                                    MonitorAutoLock& aMonitor)
    197    -> DispatchToParentResult {
    198  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
    199  mReplyMonitor.AssertCurrentThreadOwns();
    200  CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
    201  // Guard against spurious wakeups.
    202  mReceivedReply = false;
    203  // Wait for a reply
    204  do {
    205    // If the parent has been shut down, then we won't receive a reply.
    206    if (!mIPCIsAlive) {
    207      return DispatchToParentResult::DISCONNECTED;
    208    }
    209    aMonitor.Wait();
    210  } while (!mReceivedReply);
    211  return mReplySuccess ? DispatchToParentResult::SUCCESS
    212                       : DispatchToParentResult::FAILURE;
    213 }
    214 
    215 int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
    216                                       const char* deviceUniqueIdUTF8) {
    217  LOG(("%s", __PRETTY_FUNCTION__));
    218  LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8));
    219  nsCString unique_id(deviceUniqueIdUTF8);
    220  nsCOMPtr<nsIRunnable> runnable =
    221      mozilla::NewRunnableMethod<CaptureEngine, nsCString>(
    222          "camera::PCamerasChild::SendNumberOfCapabilities", this,
    223          &CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id);
    224  LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger);
    225  LOG(("Capture capability count: %d", dispatcher.ReturnValue()));
    226  return dispatcher.ReturnValue();
    227 }
    228 
    229 int CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine) {
    230  LOG(("%s", __PRETTY_FUNCTION__));
    231  nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>(
    232      "camera::PCamerasChild::SendNumberOfCaptureDevices", this,
    233      &CamerasChild::SendNumberOfCaptureDevices, aCapEngine);
    234  LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger);
    235  LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
    236  return dispatcher.ReturnValue();
    237 }
    238 
    239 mozilla::ipc::IPCResult CamerasChild::RecvReplyNumberOfCaptureDevices(
    240    const int& aDeviceCount) {
    241  LOG(("%s", __PRETTY_FUNCTION__));
    242  MonitorAutoLock monitor(mReplyMonitor);
    243  mReceivedReply = true;
    244  mReplySuccess = true;
    245  mReplyInteger = aDeviceCount;
    246  monitor.Notify();
    247  return IPC_OK();
    248 }
    249 
    250 int CamerasChild::EnsureInitialized(CaptureEngine aCapEngine) {
    251  LOG(("%s", __PRETTY_FUNCTION__));
    252  nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>(
    253      "camera::PCamerasChild::SendEnsureInitialized", this,
    254      &CamerasChild::SendEnsureInitialized, aCapEngine);
    255  LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger);
    256  LOG(("Initialized: %d", dispatcher.ReturnValue()));
    257  return dispatcher.ReturnValue();
    258 }
    259 
    260 int CamerasChild::GetCaptureCapability(
    261    CaptureEngine aCapEngine, const char* unique_idUTF8,
    262    const unsigned int capability_number,
    263    webrtc::VideoCaptureCapability* capability) {
    264  LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number));
    265  MOZ_ASSERT(capability);
    266  nsCString unique_id(unique_idUTF8);
    267  nsCOMPtr<nsIRunnable> runnable =
    268      mozilla::NewRunnableMethod<CaptureEngine, nsCString, unsigned int>(
    269          "camera::PCamerasChild::SendGetCaptureCapability", this,
    270          &CamerasChild::SendGetCaptureCapability, aCapEngine, unique_id,
    271          capability_number);
    272  mReplyCapability = capability;
    273  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    274                               kSuccess);
    275  mReplyCapability = nullptr;
    276  return dispatcher.ReturnValue();
    277 }
    278 
    279 mozilla::ipc::IPCResult CamerasChild::RecvReplyGetCaptureCapability(
    280    const VideoCaptureCapability& ipcCapability) {
    281  LOG(("%s", __PRETTY_FUNCTION__));
    282  MonitorAutoLock monitor(mReplyMonitor);
    283  mReceivedReply = true;
    284  mReplySuccess = true;
    285  mReplyCapability->width = ipcCapability.width();
    286  mReplyCapability->height = ipcCapability.height();
    287  mReplyCapability->maxFPS = ipcCapability.maxFPS();
    288  mReplyCapability->videoType =
    289      static_cast<webrtc::VideoType>(ipcCapability.videoType());
    290  mReplyCapability->interlaced = ipcCapability.interlaced();
    291  monitor.Notify();
    292  return IPC_OK();
    293 }
    294 
    295 int CamerasChild::GetCaptureDevice(
    296    CaptureEngine aCapEngine, unsigned int list_number, char* device_nameUTF8,
    297    const unsigned int device_nameUTF8Length, char* unique_idUTF8,
    298    const unsigned int unique_idUTF8Length, bool* scary) {
    299  LOG(("%s", __PRETTY_FUNCTION__));
    300  nsCOMPtr<nsIRunnable> runnable =
    301      mozilla::NewRunnableMethod<CaptureEngine, unsigned int>(
    302          "camera::PCamerasChild::SendGetCaptureDevice", this,
    303          &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number);
    304  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    305                               kSuccess);
    306  if (dispatcher.Success()) {
    307    base::strlcpy(device_nameUTF8, mReplyDeviceName.get(),
    308                  device_nameUTF8Length);
    309    base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length);
    310    if (scary) {
    311      *scary = mReplyScary;
    312    }
    313    LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8));
    314  }
    315  return dispatcher.ReturnValue();
    316 }
    317 
    318 mozilla::ipc::IPCResult CamerasChild::RecvReplyGetCaptureDevice(
    319    const nsACString& device_name, const nsACString& device_id,
    320    const bool& scary) {
    321  LOG(("%s", __PRETTY_FUNCTION__));
    322  MonitorAutoLock monitor(mReplyMonitor);
    323  mReceivedReply = true;
    324  mReplySuccess = true;
    325  mReplyDeviceName = device_name;
    326  mReplyDeviceID = device_id;
    327  mReplyScary = scary;
    328  monitor.Notify();
    329  return IPC_OK();
    330 }
    331 
    332 int CamerasChild::AllocateCapture(CaptureEngine aCapEngine,
    333                                  const char* unique_idUTF8,
    334                                  uint64_t aWindowID) {
    335  LOG(("%s", __PRETTY_FUNCTION__));
    336  nsCString unique_id(unique_idUTF8);
    337  nsCOMPtr<nsIRunnable> runnable =
    338      mozilla::NewRunnableMethod<CaptureEngine, nsCString, uint64_t>(
    339          "camera::PCamerasChild::SendAllocateCapture", this,
    340          &CamerasChild::SendAllocateCapture, aCapEngine, unique_id, aWindowID);
    341  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    342                               mReplyInteger);
    343  if (dispatcher.Success()) {
    344    LOG(("Capture Device allocated: %d", mReplyInteger));
    345  }
    346  return dispatcher.ReturnValue();
    347 }
    348 
    349 mozilla::ipc::IPCResult CamerasChild::RecvReplyAllocateCapture(
    350    const int& aCaptureId) {
    351  LOG(("%s", __PRETTY_FUNCTION__));
    352  MonitorAutoLock monitor(mReplyMonitor);
    353  mReceivedReply = true;
    354  mReplySuccess = true;
    355  mReplyInteger = aCaptureId;
    356  monitor.Notify();
    357  return IPC_OK();
    358 }
    359 
    360 int CamerasChild::ReleaseCapture(CaptureEngine aCapEngine,
    361                                 const int capture_id) {
    362  LOG(("%s", __PRETTY_FUNCTION__));
    363  nsCOMPtr<nsIRunnable> runnable =
    364      mozilla::NewRunnableMethod<CaptureEngine, int>(
    365          "camera::PCamerasChild::SendReleaseCapture", this,
    366          &CamerasChild::SendReleaseCapture, aCapEngine, capture_id);
    367  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    368                               kSuccess);
    369  return dispatcher.ReturnValue();
    370 }
    371 
    372 void CamerasChild::AddCallback(int capture_id, FrameRelay* render) {
    373  MutexAutoLock lock(mCallbackMutex);
    374  CapturerElement ce;
    375  ce.id = capture_id;
    376  ce.callback = render;
    377 
    378  if (!mCallbacks.Contains(ce, [](const auto& aLhs, const auto& aRhs) -> int {
    379        if (int res = aLhs.id - aRhs.id; res != 0) {
    380          return res;
    381        }
    382        return aLhs.callback - aRhs.callback;
    383      })) {
    384    mCallbacks.AppendElement(ce);
    385  }
    386 }
    387 
    388 void CamerasChild::RemoveCallback(const int capture_id) {
    389  MutexAutoLock lock(mCallbackMutex);
    390  for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
    391    CapturerElement ce = mCallbacks[i];
    392    if (ce.id == capture_id) {
    393      mCallbacks.RemoveElementAt(i);
    394      break;
    395    }
    396  }
    397 }
    398 
    399 int CamerasChild::StartCapture(CaptureEngine aCapEngine, const int capture_id,
    400                               const webrtc::VideoCaptureCapability& webrtcCaps,
    401                               const NormalizedConstraints& constraints,
    402                               const dom::VideoResizeModeEnum& resize_mode,
    403                               FrameRelay* cb) {
    404  LOG(("%s", __PRETTY_FUNCTION__));
    405  AddCallback(capture_id, cb);
    406  VideoCaptureCapability capCap(
    407      webrtcCaps.width, webrtcCaps.height, webrtcCaps.maxFPS,
    408      static_cast<int>(webrtcCaps.videoType), webrtcCaps.interlaced);
    409  nsCOMPtr<nsIRunnable> runnable =
    410      mozilla::NewRunnableMethod<CaptureEngine, int, VideoCaptureCapability,
    411                                 NormalizedConstraints,
    412                                 dom::VideoResizeModeEnum>(
    413          "camera::PCamerasChild::SendStartCapture", this,
    414          &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap,
    415          constraints, resize_mode);
    416  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    417                               kSuccess);
    418  return dispatcher.ReturnValue();
    419 }
    420 
    421 int CamerasChild::FocusOnSelectedSource(CaptureEngine aCapEngine,
    422                                        const int aCaptureId) {
    423  LOG(("%s", __PRETTY_FUNCTION__));
    424  nsCOMPtr<nsIRunnable> runnable =
    425      mozilla::NewRunnableMethod<CaptureEngine, int>(
    426          "camera::PCamerasChild::SendFocusOnSelectedSource", this,
    427          &CamerasChild::SendFocusOnSelectedSource, aCapEngine, aCaptureId);
    428  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    429                               kSuccess);
    430  return dispatcher.ReturnValue();
    431 }
    432 
    433 int CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) {
    434  LOG(("%s", __PRETTY_FUNCTION__));
    435  nsCOMPtr<nsIRunnable> runnable =
    436      mozilla::NewRunnableMethod<CaptureEngine, int>(
    437          "camera::PCamerasChild::SendStopCapture", this,
    438          &CamerasChild::SendStopCapture, aCapEngine, capture_id);
    439  LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError,
    440                               kSuccess);
    441  if (dispatcher.Success() || dispatcher.Disconnected()) {
    442    RemoveCallback(capture_id);
    443  }
    444  return dispatcher.ReturnValue();
    445 }
    446 
    447 class ShutdownRunnable : public Runnable {
    448 public:
    449  explicit ShutdownRunnable(already_AddRefed<Runnable>&& aReplyEvent)
    450      : Runnable("camera::ShutdownRunnable"), mReplyEvent(aReplyEvent) {};
    451 
    452  NS_IMETHOD Run() override {
    453    LOG(("Closing BackgroundChild"));
    454    // This will also destroy the CamerasChild.
    455    ipc::BackgroundChild::CloseForCurrentThread();
    456 
    457    NS_DispatchToMainThread(mReplyEvent.forget());
    458 
    459    return NS_OK;
    460  }
    461 
    462 private:
    463  RefPtr<Runnable> mReplyEvent;
    464 };
    465 
    466 void Shutdown(void) {
    467  // Called from both MediaEngineWebRTC::Shutdown() on the MediaManager thread
    468  // and DeallocPCamerasChild() on the dedicated IPC thread.
    469  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
    470 
    471  CamerasChild* child = CamerasSingleton::Child();
    472  if (!child) {
    473    // We don't want to cause everything to get fired up if we're
    474    // really already shut down.
    475    LOG(("Shutdown when already shut down"));
    476    return;
    477  }
    478  if (CamerasSingleton::Thread()) {
    479    LOG(("PBackground thread exists, dispatching close"));
    480    // The IPC thread is shut down on the main thread after the
    481    // BackgroundChild is closed.
    482    RefPtr<ShutdownRunnable> runnable = new ShutdownRunnable(
    483        NewRunnableMethod("nsIThread::Shutdown", CamerasSingleton::Thread(),
    484                          &nsIThread::Shutdown));
    485    CamerasSingleton::Thread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
    486  } else {
    487    LOG(("Shutdown called without PBackground thread"));
    488  }
    489  LOG(("Erasing sCameras & thread refs (original thread)"));
    490  CamerasSingleton::Child() = nullptr;
    491  CamerasSingleton::Thread() = nullptr;
    492 }
    493 
    494 mozilla::ipc::IPCResult CamerasChild::RecvCaptureEnded(
    495    nsTArray<int>&& aCaptureIds) {
    496  MutexAutoLock lock(mCallbackMutex);
    497  for (int capId : aCaptureIds) {
    498    if (auto* cb = Callback(capId)) {
    499      cb->OnCaptureEnded();
    500    } else {
    501      LOG(("CaptureEnded called with dead callback"));
    502    }
    503  }
    504  return IPC_OK();
    505 }
    506 
    507 mozilla::ipc::IPCResult CamerasChild::RecvDeliverFrame(
    508    const int& aCaptureId, nsTArray<int>&& aStreamIds,
    509    mozilla::ipc::Shmem&& aShmem, const VideoFrameProperties& aProps) {
    510  MutexAutoLock lock(mCallbackMutex);
    511  for (const int& streamId : aStreamIds) {
    512    if (auto* cb = Callback(streamId)) {
    513      unsigned char* image = aShmem.get<unsigned char>();
    514      cb->DeliverFrame(image, aProps);
    515    } else {
    516      LOG(("DeliverFrame called with dead callback"));
    517    }
    518  }
    519  SendReleaseFrame(aCaptureId, std::move(aShmem));
    520  return IPC_OK();
    521 }
    522 
    523 mozilla::ipc::IPCResult CamerasChild::RecvDeviceChange() {
    524  mDeviceListChangeEvent.Notify();
    525  return IPC_OK();
    526 }
    527 
    528 void CamerasChild::ActorDestroy(ActorDestroyReason aWhy) {
    529  LOG(("ActorDestroy"));
    530  MonitorAutoLock monitor(mReplyMonitor);
    531  mIPCIsAlive = false;
    532  // Hopefully prevent us from getting stuck
    533  // on replies that'll never come.
    534  monitor.NotifyAll();
    535 }
    536 
    537 CamerasChild::CamerasChild()
    538    : mCallbackMutex("mozilla::cameras::CamerasChild::mCallbackMutex"),
    539      mIPCIsAlive(true),
    540      mRequestMutex("mozilla::cameras::CamerasChild::mRequestMutex"),
    541      mReplyMonitor("mozilla::cameras::CamerasChild::mReplyMonitor"),
    542      mReceivedReply(false),
    543      mReplySuccess(false),
    544      mReplyInteger(0),
    545      mReplyScary(false) {
    546  LOG(("CamerasChild: %p", this));
    547 
    548  MOZ_COUNT_CTOR(CamerasChild);
    549 }
    550 
    551 CamerasChild::~CamerasChild() {
    552  LOG(("~CamerasChild: %p", this));
    553  CamerasSingleton::AssertNoChild();
    554  MOZ_COUNT_DTOR(CamerasChild);
    555 }
    556 
    557 FrameRelay* CamerasChild::Callback(int capture_id) {
    558  for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
    559    CapturerElement ce = mCallbacks[i];
    560    if (ce.id == capture_id) {
    561      return ce.callback;
    562    }
    563  }
    564 
    565  return nullptr;
    566 }
    567 
    568 }  // namespace mozilla::camera
    569 
    570 #undef LOG
    571 #undef LOG_ENABLED