diff options
Diffstat (limited to 'dom/media/systemservices/CamerasChild.cpp')
-rw-r--r-- | dom/media/systemservices/CamerasChild.cpp | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/dom/media/systemservices/CamerasChild.cpp b/dom/media/systemservices/CamerasChild.cpp new file mode 100644 index 000000000..0f7d1c1df --- /dev/null +++ b/dom/media/systemservices/CamerasChild.cpp @@ -0,0 +1,724 @@ +/* -*- 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" + +#include "webrtc/video_engine/include/vie_capture.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) + +#define FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS 5000 +#define FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT 30 + +namespace mozilla { +namespace camera { + +CamerasSingleton::CamerasSingleton() + : mCamerasMutex("CamerasSingleton::mCamerasMutex"), + mCameras(nullptr), + mCamerasChildThread(nullptr), + mFakeDeviceChangeEventThread(nullptr) { + LOG(("CamerasSingleton: %p", this)); +} + +CamerasSingleton::~CamerasSingleton() { + LOG(("~CamerasSingleton: %p", this)); +} + +class FakeOnDeviceChangeEventRunnable : public Runnable +{ +public: + explicit FakeOnDeviceChangeEventRunnable(uint8_t counter) + : mCounter(counter) {} + + NS_IMETHOD Run() override + { + OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex()); + + CamerasChild* child = CamerasSingleton::Child(); + if (child) { + child->OnDeviceChange(); + + if (mCounter++ < FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT) { + RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(mCounter); + CamerasSingleton::FakeDeviceChangeEventThread()->DelayedDispatch(evt.forget(), + FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS); + } + } + + return NS_OK; + } + +private: + uint8_t mCounter; +}; + +class InitializeIPCThread : public Runnable +{ +public: + InitializeIPCThread() + : mCamerasChild(nullptr) {} + + NS_IMETHOD Run() override { + // Try to get the PBackground handle + ipc::PBackgroundChild* existingBackgroundChild = + ipc::BackgroundChild::GetForCurrentThread(); + // If it's not spun up yet, block until it is, and retry + if (!existingBackgroundChild) { + LOG(("No existingBackgroundChild")); + existingBackgroundChild = + ipc::BackgroundChild::SynchronouslyCreateForCurrentThread(); + 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()); + + 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::SynchronouslyCreateForCurrentThread 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(); +} + +int CamerasChild::AddDeviceChangeCallback(DeviceChangeCallback* aCallback) +{ + // According to the spec, if the script sets + // navigator.mediaDevices.ondevicechange and the permission state is + // "always granted", the User Agent MUST fires a devicechange event when + // a new media input device is made available, even the script never + // call getusermedia or enumerateDevices. + + // In order to detect the event, we need to init the camera engine. + // Currently EnsureInitialized(aCapEngine) is only called when one of + // CamerasaParent api, e.g., RecvNumberOfCaptureDevices(), is called. + + // So here we setup camera engine via EnsureInitialized(aCapEngine) + + EnsureInitialized(CameraEngine); + return DeviceChangeCallback::AddDeviceChangeCallback(aCallback); +} + +bool +CamerasChild::RecvReplyFailure(void) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = false; + monitor.Notify(); + return true; +} + +bool +CamerasChild::RecvReplySuccess(void) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = true; + monitor.Notify(); + return true; +} + +bool +CamerasChild::RecvReplyNumberOfCapabilities(const int& numdev) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = true; + mReplyInteger = numdev; + monitor.Notify(); + return true; +} + +// 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. +template <class T = int> +class LockAndDispatch +{ +public: + LockAndDispatch(CamerasChild* aCamerasChild, + const char* aRequestingFunc, + nsIRunnable *aRunnable, + const T& aFailureValue = T(-1), const T& aSuccessValue = T(0)) + : mCamerasChild(aCamerasChild), mRequestingFunc(aRequestingFunc), + mRunnable(aRunnable), + mReplyLock(aCamerasChild->mReplyMonitor), + mRequestLock(aCamerasChild->mRequestMutex), + mSuccess(true), + mFailureValue(aFailureValue), mSuccessValue(aSuccessValue) + { + Dispatch(); + } + + const 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(); + CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL); + // We can't see if the send worked, so we need to be able to bail + // out on shutdown (when it failed and we won't get a reply). + if (!mIPCIsAlive) { + return false; + } + // Guard against spurious wakeups. + mReceivedReply = false; + // Wait for a reply + do { + aMonitor.Wait(); + } while (!mReceivedReply && mIPCIsAlive); + if (!mReplySuccess) { + return false; + } + return true; +} + +int +CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine, + const char* deviceUniqueIdUTF8) +{ + LOG((__PRETTY_FUNCTION__)); + LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8)); + nsCString unique_id(deviceUniqueIdUTF8); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString> + (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((__PRETTY_FUNCTION__)); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine> + (this, &CamerasChild::SendNumberOfCaptureDevices, aCapEngine); + LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger); + LOG(("Capture Devices: %d", dispatcher.ReturnValue())); + return dispatcher.ReturnValue(); +} + +bool +CamerasChild::RecvReplyNumberOfCaptureDevices(const int& numdev) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = true; + mReplyInteger = numdev; + monitor.Notify(); + return true; +} + +int +CamerasChild::EnsureInitialized(CaptureEngine aCapEngine) +{ + LOG((__PRETTY_FUNCTION__)); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine> + (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::CaptureCapability& capability) +{ + LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number)); + nsCString unique_id(unique_idUTF8); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, unsigned int> + (this, &CamerasChild::SendGetCaptureCapability, aCapEngine, unique_id, capability_number); + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + capability = mReplyCapability; + } + return dispatcher.ReturnValue(); +} + +bool +CamerasChild::RecvReplyGetCaptureCapability(const CaptureCapability& ipcCapability) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = true; + mReplyCapability.width = ipcCapability.width(); + mReplyCapability.height = ipcCapability.height(); + mReplyCapability.maxFPS = ipcCapability.maxFPS(); + mReplyCapability.expectedCaptureDelay = ipcCapability.expectedCaptureDelay(); + mReplyCapability.rawType = static_cast<webrtc::RawVideoType>(ipcCapability.rawType()); + mReplyCapability.codecType = static_cast<webrtc::VideoCodecType>(ipcCapability.codecType()); + mReplyCapability.interlaced = ipcCapability.interlaced(); + monitor.Notify(); + return true; +} + +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) +{ + LOG((__PRETTY_FUNCTION__)); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, unsigned int> + (this, &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number); + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length); + base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length); + if (scary) { + *scary = mReplyScary; + } + LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8)); + } + return dispatcher.ReturnValue(); +} + +bool +CamerasChild::RecvReplyGetCaptureDevice(const nsCString& device_name, + const nsCString& device_id, + const bool& scary) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = true; + mReplyDeviceName = device_name; + mReplyDeviceID = device_id; + mReplyScary = scary; + monitor.Notify(); + return true; +} + +int +CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine, + const char* unique_idUTF8, + const unsigned int unique_idUTF8Length, + int& capture_id, + const nsACString& aOrigin) +{ + LOG((__PRETTY_FUNCTION__)); + nsCString unique_id(unique_idUTF8); + nsCString origin(aOrigin); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, nsCString> + (this, &CamerasChild::SendAllocateCaptureDevice, aCapEngine, unique_id, origin); + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + LOG(("Capture Device allocated: %d", mReplyInteger)); + capture_id = mReplyInteger; + } + return dispatcher.ReturnValue(); +} + + +bool +CamerasChild::RecvReplyAllocateCaptureDevice(const int& numdev) +{ + LOG((__PRETTY_FUNCTION__)); + MonitorAutoLock monitor(mReplyMonitor); + mReceivedReply = true; + mReplySuccess = true; + mReplyInteger = numdev; + monitor.Notify(); + return true; +} + +int +CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine, + const int capture_id) +{ + LOG((__PRETTY_FUNCTION__)); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, int> + (this, &CamerasChild::SendReleaseCaptureDevice, aCapEngine, capture_id); + LockAndDispatch<> dispatcher(this, __func__, runnable); + return dispatcher.ReturnValue(); +} + +void +CamerasChild::AddCallback(const CaptureEngine aCapEngine, const int capture_id, + webrtc::ExternalRenderer* render) +{ + MutexAutoLock lock(mCallbackMutex); + CapturerElement ce; + ce.engine = aCapEngine; + ce.id = capture_id; + ce.callback = render; + mCallbacks.AppendElement(ce); +} + +void +CamerasChild::RemoveCallback(const CaptureEngine aCapEngine, const int capture_id) +{ + MutexAutoLock lock(mCallbackMutex); + for (unsigned int i = 0; i < mCallbacks.Length(); i++) { + CapturerElement ce = mCallbacks[i]; + if (ce.engine == aCapEngine && ce.id == capture_id) { + mCallbacks.RemoveElementAt(i); + break; + } + } +} + +int +CamerasChild::StartCapture(CaptureEngine aCapEngine, + const int capture_id, + webrtc::CaptureCapability& webrtcCaps, + webrtc::ExternalRenderer* cb) +{ + LOG((__PRETTY_FUNCTION__)); + AddCallback(aCapEngine, capture_id, cb); + CaptureCapability capCap(webrtcCaps.width, + webrtcCaps.height, + webrtcCaps.maxFPS, + webrtcCaps.expectedCaptureDelay, + webrtcCaps.rawType, + webrtcCaps.codecType, + webrtcCaps.interlaced); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, int, CaptureCapability> + (this, &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap); + LockAndDispatch<> dispatcher(this, __func__, runnable); + return dispatcher.ReturnValue(); +} + +int +CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) +{ + LOG((__PRETTY_FUNCTION__)); + nsCOMPtr<nsIRunnable> runnable = + mozilla::NewNonOwningRunnableMethod<CaptureEngine, int> + (this, &CamerasChild::SendStopCapture, aCapEngine, capture_id); + LockAndDispatch<> dispatcher(this, __func__, runnable); + if (dispatcher.Success()) { + RemoveCallback(aCapEngine, capture_id); + } + return dispatcher.ReturnValue(); +} + +void +Shutdown(void) +{ + 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; + } + child->ShutdownAll(); +} + +class ShutdownRunnable : public Runnable { +public: + explicit + ShutdownRunnable(already_AddRefed<Runnable>&& aReplyEvent) + : mReplyEvent(aReplyEvent) {}; + + NS_IMETHOD Run() override { + LOG(("Closing BackgroundChild")); + ipc::BackgroundChild::CloseForCurrentThread(); + + NS_DispatchToMainThread(mReplyEvent.forget()); + + return NS_OK; + } + +private: + RefPtr<Runnable> mReplyEvent; +}; + +void +CamerasChild::ShutdownAll() +{ + // Called with CamerasSingleton::Mutex() held + ShutdownParent(); + ShutdownChild(); +} + +void +CamerasChild::ShutdownParent() +{ + // Called with CamerasSingleton::Mutex() held + { + MonitorAutoLock monitor(mReplyMonitor); + mIPCIsAlive = false; + monitor.NotifyAll(); + } + if (CamerasSingleton::Thread()) { + LOG(("Dispatching actor deletion")); + // Delete the parent actor. + // CamerasChild (this) will remain alive and is only deleted by the + // IPC layer when SendAllDone returns. + nsCOMPtr<nsIRunnable> deleteRunnable = + mozilla::NewNonOwningRunnableMethod(this, &CamerasChild::SendAllDone); + CamerasSingleton::Thread()->Dispatch(deleteRunnable, NS_DISPATCH_NORMAL); + } else { + LOG(("ShutdownParent called without PBackground thread")); + } +} + +void +CamerasChild::ShutdownChild() +{ + // Called with CamerasSingleton::Mutex() held + if (CamerasSingleton::Thread()) { + LOG(("PBackground thread exists, dispatching close")); + // Dispatch closing the IPC thread back to us when the + // BackgroundChild is closed. + RefPtr<ShutdownRunnable> runnable = + new ShutdownRunnable(NewRunnableMethod(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; + + if (CamerasSingleton::FakeDeviceChangeEventThread()) { + RefPtr<ShutdownRunnable> runnable = + new ShutdownRunnable(NewRunnableMethod(CamerasSingleton::FakeDeviceChangeEventThread(), + &nsIThread::Shutdown)); + CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); + } + CamerasSingleton::FakeDeviceChangeEventThread() = nullptr; +} + +bool +CamerasChild::RecvDeliverFrame(const CaptureEngine& capEngine, + const int& capId, + mozilla::ipc::Shmem&& shmem, + const size_t& size, + const uint32_t& time_stamp, + const int64_t& ntp_time, + const int64_t& render_time) +{ + MutexAutoLock lock(mCallbackMutex); + if (Callback(capEngine, capId)) { + unsigned char* image = shmem.get<unsigned char>(); + Callback(capEngine, capId)->DeliverFrame(image, size, + time_stamp, + ntp_time, render_time, + nullptr); + } else { + LOG(("DeliverFrame called with dead callback")); + } + SendReleaseFrame(shmem); + return true; +} + +bool +CamerasChild::RecvDeviceChange() +{ + this->OnDeviceChange(); + return true; +} + +int +CamerasChild::SetFakeDeviceChangeEvents() +{ + CamerasSingleton::Mutex().AssertCurrentThreadOwns(); + + if(!CamerasSingleton::FakeDeviceChangeEventThread()) { + nsresult rv = NS_NewNamedThread("Fake DC Event", + getter_AddRefs(CamerasSingleton::FakeDeviceChangeEventThread())); + if (NS_FAILED(rv)) { + LOG(("Error launching Fake OnDeviceChange Event Thread")); + return -1; + } + } + + // To simulate the devicechange event in mochitest, + // we fire a fake devicechange event in Camera IPC thread periodically + RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0); + CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL); + + return 0; +} + +bool +CamerasChild::RecvFrameSizeChange(const CaptureEngine& capEngine, + const int& capId, + const int& w, const int& h) +{ + LOG((__PRETTY_FUNCTION__)); + MutexAutoLock lock(mCallbackMutex); + if (Callback(capEngine, capId)) { + Callback(capEngine, capId)->FrameSizeChange(w, h, 0); + } else { + LOG(("Frame size change with dead callback")); + } + return true; +} + +void +CamerasChild::ActorDestroy(ActorDestroyReason aWhy) +{ + 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") +{ + LOG(("CamerasChild: %p", this)); + + MOZ_COUNT_CTOR(CamerasChild); +} + +CamerasChild::~CamerasChild() +{ + LOG(("~CamerasChild: %p", this)); + + { + OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex()); + // In normal circumstances we've already shut down and the + // following does nothing. But on fatal IPC errors we will + // get destructed immediately, and should not try to reach + // the parent. + ShutdownChild(); + } + + MOZ_COUNT_DTOR(CamerasChild); +} + +webrtc::ExternalRenderer* CamerasChild::Callback(CaptureEngine aCapEngine, + int capture_id) +{ + for (unsigned int i = 0; i < mCallbacks.Length(); i++) { + CapturerElement ce = mCallbacks[i]; + if (ce.engine == aCapEngine && ce.id == capture_id) { + return ce.callback; + } + } + + return nullptr; +} + +} +} |