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