summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices/CamerasChild.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices/CamerasChild.h')
-rw-r--r--dom/media/systemservices/CamerasChild.h262
1 files changed, 262 insertions, 0 deletions
diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h
new file mode 100644
index 0000000000..18bdeec251
--- /dev/null
+++ b/dom/media/systemservices/CamerasChild.h
@@ -0,0 +1,262 @@
+/* -*- 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/. */
+
+#ifndef mozilla_CamerasChild_h
+#define mozilla_CamerasChild_h
+
+#include <utility>
+
+#include "MediaEventSource.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/camera/PCamerasChild.h"
+#include "mozilla/camera/PCamerasParent.h"
+#include "nsCOMPtr.h"
+
+// conflicts with #include of scoped_ptr.h
+#undef FF
+#include "modules/video_capture/video_capture_defines.h"
+
+namespace mozilla {
+
+namespace ipc {
+class BackgroundChildImpl;
+} // namespace ipc
+
+namespace camera {
+
+class FrameRelay {
+ public:
+ virtual int DeliverFrame(
+ uint8_t* buffer, const mozilla::camera::VideoFrameProperties& props) = 0;
+};
+
+struct CapturerElement {
+ CaptureEngine engine;
+ int id;
+ FrameRelay* callback;
+};
+
+// Forward declaration so we can work with pointers to it.
+class CamerasChild;
+// Helper class in impl that we friend.
+template <class T>
+class LockAndDispatch;
+
+// We emulate the sync webrtc.org API with the help of singleton
+// CamerasSingleton, which manages a pointer to an IPC object, a thread
+// where IPC operations should run on, and a mutex.
+// The static function Cameras() will use that Singleton to set up,
+// if needed, both the thread and the associated IPC objects and return
+// a pointer to the IPC object. Users can then do IPC calls on that object
+// after dispatching them to aforementioned thread.
+
+// 2 Threads are involved in this code:
+// - the MediaManager thread, which will call the (static, sync API) functions
+// through MediaEngineRemoteVideoSource
+// - the Cameras IPC thread, which will be doing our IPC to the parent process
+// via PBackground
+
+// Our main complication is that we emulate a sync API while (having to do)
+// async messaging. We dispatch the messages to another thread to send them
+// async and hold a Monitor to wait for the result to be asynchronously received
+// again. The requirement for async messaging originates on the parent side:
+// it's not reasonable to block all PBackground IPC there while waiting for
+// something like device enumeration to complete.
+
+class CamerasSingleton {
+ public:
+ static OffTheBooksMutex& Mutex() { return singleton().mCamerasMutex; }
+
+ static CamerasChild*& Child() {
+ Mutex().AssertCurrentThreadOwns();
+ return singleton().mCameras;
+ }
+
+ static nsCOMPtr<nsIThread>& Thread() {
+ Mutex().AssertCurrentThreadOwns();
+ return singleton().mCamerasChildThread;
+ }
+ // The mutex is not held because mCameras is known not to be modified
+ // concurrently when this is asserted.
+ static void AssertNoChild() { MOZ_ASSERT(!singleton().mCameras); }
+
+ private:
+ CamerasSingleton();
+ ~CamerasSingleton();
+
+ static CamerasSingleton& singleton() {
+ static CamerasSingleton camera;
+ return camera;
+ }
+
+ // Reinitializing CamerasChild will change the pointers below.
+ // We don't want this to happen in the middle of preparing IPC.
+ // We will be alive on destruction, so this needs to be off the books.
+ mozilla::OffTheBooksMutex mCamerasMutex;
+
+ // This is owned by the IPC code, and the same code controls the lifetime.
+ // It will set and clear this pointer as appropriate in setup/teardown.
+ // We'd normally make this a WeakPtr but unfortunately the IPC code already
+ // uses the WeakPtr mixin in a protected base class of CamerasChild, and in
+ // any case the object becomes unusable as soon as IPC is tearing down, which
+ // will be before actual destruction.
+ CamerasChild* mCameras;
+ nsCOMPtr<nsIThread> mCamerasChildThread;
+};
+
+// Get a pointer to a CamerasChild object we can use to do IPC with.
+// This does everything needed to set up, including starting the IPC
+// channel with PBackground, blocking until thats done, and starting the
+// thread to do IPC on. This will fail if we're in shutdown. On success
+// it will set up the CamerasSingleton.
+CamerasChild* GetCamerasChild();
+
+CamerasChild* GetCamerasChildIfExists();
+
+// Shut down the IPC channel and everything associated, like WebRTC.
+// This is a static call because the CamerasChild object may not even
+// be alive when we're called.
+void Shutdown(void);
+
+// Obtain the CamerasChild object (if possible, i.e. not shutting down),
+// and maintain a grip on the object for the duration of the call.
+template <class MEM_FUN, class... ARGS>
+int GetChildAndCall(MEM_FUN&& f, ARGS&&... args) {
+ OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+ CamerasChild* child = GetCamerasChild();
+ if (child) {
+ return (child->*f)(std::forward<ARGS>(args)...);
+ } else {
+ return -1;
+ }
+}
+
+class CamerasChild final : public PCamerasChild {
+ friend class mozilla::ipc::BackgroundChildImpl;
+ template <class T>
+ friend class mozilla::camera::LockAndDispatch;
+
+ public:
+ // We are owned by the PBackground thread only. CamerasSingleton
+ // takes a non-owning reference.
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CamerasChild)
+
+ // IPC messages recevied, received on the PBackground thread
+ // these are the actual callbacks with data
+ mozilla::ipc::IPCResult RecvDeliverFrame(
+ const CaptureEngine&, const int&, mozilla::ipc::Shmem&&,
+ const VideoFrameProperties& prop) override;
+
+ mozilla::ipc::IPCResult RecvDeviceChange() override;
+
+ // these are response messages to our outgoing requests
+ mozilla::ipc::IPCResult RecvReplyNumberOfCaptureDevices(const int&) override;
+ mozilla::ipc::IPCResult RecvReplyNumberOfCapabilities(const int&) override;
+ mozilla::ipc::IPCResult RecvReplyAllocateCapture(const int&) override;
+ mozilla::ipc::IPCResult RecvReplyGetCaptureCapability(
+ const VideoCaptureCapability& capability) override;
+ mozilla::ipc::IPCResult RecvReplyGetCaptureDevice(
+ const nsACString& device_name, const nsACString& device_id,
+ const bool& scary) override;
+ mozilla::ipc::IPCResult RecvReplyFailure(void) override;
+ mozilla::ipc::IPCResult RecvReplySuccess(void) override;
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // the webrtc.org ViECapture calls are mirrored here, but with access
+ // to a specific PCameras instance to communicate over. These also
+ // run on the MediaManager thread
+ int NumberOfCaptureDevices(CaptureEngine aCapEngine);
+ int NumberOfCapabilities(CaptureEngine aCapEngine,
+ const char* deviceUniqueIdUTF8);
+ int ReleaseCapture(CaptureEngine aCapEngine, const int capture_id);
+ int StartCapture(CaptureEngine aCapEngine, const int capture_id,
+ const webrtc::VideoCaptureCapability& capability,
+ FrameRelay* func);
+ int FocusOnSelectedSource(CaptureEngine aCapEngine, const int capture_id);
+ int StopCapture(CaptureEngine aCapEngine, const int capture_id);
+ // Returns a non-negative capture identifier or -1 on failure.
+ int AllocateCapture(CaptureEngine aCapEngine, const char* unique_idUTF8,
+ uint64_t aWindowID);
+ int GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8,
+ const unsigned int capability_number,
+ webrtc::VideoCaptureCapability* capability);
+ int 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 = nullptr);
+ int EnsureInitialized(CaptureEngine aCapEngine);
+
+ template <typename This>
+ int ConnectDeviceListChangeListener(MediaEventListener* aListener,
+ AbstractThread* aTarget, This* aThis,
+ void (This::*aMethod)()) {
+ // 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
+ // CamerasParent api, e.g., RecvNumberOfCaptureDevices(), is called.
+
+ // So here we setup camera engine via EnsureInitialized(aCapEngine)
+
+ EnsureInitialized(CameraEngine);
+ *aListener = mDeviceListChangeEvent.Connect(aTarget, aThis, aMethod);
+ return IPC_OK();
+ }
+
+ FrameRelay* Callback(CaptureEngine aCapEngine, int capture_id);
+
+ private:
+ CamerasChild();
+ ~CamerasChild();
+ // Dispatch a Runnable to the PCamerasParent, by executing it on the
+ // decidecated Cameras IPC/PBackground thread.
+ bool DispatchToParent(nsIRunnable* aRunnable, MonitorAutoLock& aMonitor);
+ void AddCallback(const CaptureEngine aCapEngine, const int capture_id,
+ FrameRelay* render);
+ void RemoveCallback(const CaptureEngine aCapEngine, const int capture_id);
+
+ nsTArray<CapturerElement> mCallbacks;
+ // Protects the callback arrays
+ Mutex mCallbackMutex MOZ_UNANNOTATED;
+
+ bool mIPCIsAlive;
+
+ // Hold to prevent multiple outstanding requests. We don't use
+ // request IDs so we only support one at a time. Don't want try
+ // to use the webrtc.org API from multiple threads simultanously.
+ // The monitor below isn't sufficient for this, as it will drop
+ // the lock when Wait-ing for a response, allowing us to send a new
+ // request. The Notify on receiving the response will then unblock
+ // both waiters and one will be guaranteed to get the wrong result.
+ // Take this one before taking mReplyMonitor.
+ Mutex mRequestMutex MOZ_UNANNOTATED;
+ // Hold to wait for an async response to our calls *and* until the
+ // user of LockAndDispatch<> has read the data out. This is done by
+ // keeping the LockAndDispatch object alive.
+ Monitor mReplyMonitor MOZ_UNANNOTATED;
+ // Async response valid?
+ bool mReceivedReply;
+ // Async responses data contents;
+ bool mReplySuccess;
+ const int mZero;
+ int mReplyInteger;
+ webrtc::VideoCaptureCapability* mReplyCapability = nullptr;
+ nsCString mReplyDeviceName;
+ nsCString mReplyDeviceID;
+ bool mReplyScary;
+ MediaEventProducer<void> mDeviceListChangeEvent;
+};
+
+} // namespace camera
+} // namespace mozilla
+
+#endif // mozilla_CamerasChild_h