CamerasChild.h (10914B)
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 #ifndef mozilla_CamerasChild_h 8 #define mozilla_CamerasChild_h 9 10 #include <utility> 11 12 #include "MediaEventSource.h" 13 #include "mozilla/Mutex.h" 14 #include "mozilla/camera/PCamerasChild.h" 15 #include "mozilla/camera/PCamerasParent.h" 16 #include "nsCOMPtr.h" 17 18 // conflicts with #include of scoped_ptr.h 19 #undef FF 20 #include "modules/video_capture/video_capture_defines.h" 21 22 namespace mozilla { 23 24 namespace ipc { 25 class BackgroundChildImpl; 26 } // namespace ipc 27 28 namespace camera { 29 30 class FrameRelay { 31 public: 32 virtual void OnCaptureEnded() = 0; 33 virtual int DeliverFrame( 34 uint8_t* buffer, const mozilla::camera::VideoFrameProperties& props) = 0; 35 }; 36 37 struct CapturerElement { 38 int id{}; 39 FrameRelay* callback{}; 40 }; 41 42 // Forward declaration so we can work with pointers to it. 43 class CamerasChild; 44 // Helper class in impl that we friend. 45 template <class T> 46 class LockAndDispatch; 47 48 static constexpr int kSuccess = 0; 49 static constexpr int kError = -1; 50 static constexpr int kIpcError = -2; 51 52 // We emulate the sync webrtc.org API with the help of singleton 53 // CamerasSingleton, which manages a pointer to an IPC object, a thread 54 // where IPC operations should run on, and a mutex. 55 // The static function Cameras() will use that Singleton to set up, 56 // if needed, both the thread and the associated IPC objects and return 57 // a pointer to the IPC object. Users can then do IPC calls on that object 58 // after dispatching them to aforementioned thread. 59 60 // 2 Threads are involved in this code: 61 // - the MediaManager thread, which will call the (static, sync API) functions 62 // through MediaEngineRemoteVideoSource 63 // - the Cameras IPC thread, which will be doing our IPC to the parent process 64 // via PBackground 65 66 // Our main complication is that we emulate a sync API while (having to do) 67 // async messaging. We dispatch the messages to another thread to send them 68 // async and hold a Monitor to wait for the result to be asynchronously received 69 // again. The requirement for async messaging originates on the parent side: 70 // it's not reasonable to block all PBackground IPC there while waiting for 71 // something like device enumeration to complete. 72 73 class CamerasSingleton { 74 public: 75 static OffTheBooksMutex& Mutex() { return singleton().mCamerasMutex; } 76 77 static CamerasChild*& Child() { 78 Mutex().AssertCurrentThreadOwns(); 79 return singleton().mCameras; 80 } 81 82 static nsCOMPtr<nsIThread>& Thread() { 83 Mutex().AssertCurrentThreadOwns(); 84 return singleton().mCamerasChildThread; 85 } 86 // The mutex is not held because mCameras is known not to be modified 87 // concurrently when this is asserted. 88 static void AssertNoChild() { MOZ_ASSERT(!singleton().mCameras); } 89 90 private: 91 CamerasSingleton(); 92 ~CamerasSingleton(); 93 94 static CamerasSingleton& singleton() { 95 static CamerasSingleton camera; 96 return camera; 97 } 98 99 // Reinitializing CamerasChild will change the pointers below. 100 // We don't want this to happen in the middle of preparing IPC. 101 // We will be alive on destruction, so this needs to be off the books. 102 mozilla::OffTheBooksMutex mCamerasMutex; 103 104 // This is owned by the IPC code, and the same code controls the lifetime. 105 // It will set and clear this pointer as appropriate in setup/teardown. 106 // We'd normally make this a WeakPtr but unfortunately the IPC code already 107 // uses the WeakPtr mixin in a protected base class of CamerasChild, and in 108 // any case the object becomes unusable as soon as IPC is tearing down, which 109 // will be before actual destruction. 110 CamerasChild* mCameras; 111 nsCOMPtr<nsIThread> mCamerasChildThread; 112 }; 113 114 // Get a pointer to a CamerasChild object we can use to do IPC with. 115 // This does everything needed to set up, including starting the IPC 116 // channel with PBackground, blocking until thats done, and starting the 117 // thread to do IPC on. This will fail if we're in shutdown. On success 118 // it will set up the CamerasSingleton. 119 CamerasChild* GetCamerasChild(); 120 121 CamerasChild* GetCamerasChildIfExists(); 122 123 // Shut down the IPC channel and everything associated, like WebRTC. 124 // This is a static call because the CamerasChild object may not even 125 // be alive when we're called. 126 void Shutdown(void); 127 128 // Obtain the CamerasChild object (if possible, i.e. not shutting down), 129 // and maintain a grip on the object for the duration of the call. 130 template <class MEM_FUN, class... ARGS> 131 int GetChildAndCall(MEM_FUN&& f, ARGS&&... args) { 132 OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex()); 133 CamerasChild* child = GetCamerasChild(); 134 if (child) { 135 return (child->*f)(std::forward<ARGS>(args)...); 136 } else { 137 return -1; 138 } 139 } 140 141 class CamerasChild final : public PCamerasChild { 142 friend class mozilla::ipc::BackgroundChildImpl; 143 template <class T> 144 friend class mozilla::camera::LockAndDispatch; 145 146 public: 147 // We are owned by the PBackground thread only. CamerasSingleton 148 // takes a non-owning reference. 149 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CamerasChild) 150 151 // IPC messages recevied, received on the PBackground thread 152 // these are the actual callbacks with data 153 mozilla::ipc::IPCResult RecvCaptureEnded( 154 nsTArray<int>&& aCaptureIds) override; 155 mozilla::ipc::IPCResult RecvDeliverFrame( 156 const int& aCaptureId, nsTArray<int>&& aStreamIds, 157 mozilla::ipc::Shmem&& aShmem, 158 const VideoFrameProperties& aProps) override; 159 160 mozilla::ipc::IPCResult RecvDeviceChange() override; 161 162 // these are response messages to our outgoing requests 163 mozilla::ipc::IPCResult RecvReplyNumberOfCaptureDevices(const int&) override; 164 mozilla::ipc::IPCResult RecvReplyNumberOfCapabilities(const int&) override; 165 mozilla::ipc::IPCResult RecvReplyAllocateCapture(const int&) override; 166 mozilla::ipc::IPCResult RecvReplyGetCaptureCapability( 167 const VideoCaptureCapability& capability) override; 168 mozilla::ipc::IPCResult RecvReplyGetCaptureDevice( 169 const nsACString& device_name, const nsACString& device_id, 170 const bool& scary) override; 171 mozilla::ipc::IPCResult RecvReplyFailure(void) override; 172 mozilla::ipc::IPCResult RecvReplySuccess(void) override; 173 void ActorDestroy(ActorDestroyReason aWhy) override; 174 175 // the webrtc.org ViECapture calls are mirrored here, but with access 176 // to a specific PCameras instance to communicate over. These also 177 // run on the MediaManager thread 178 int NumberOfCaptureDevices(CaptureEngine aCapEngine); 179 int NumberOfCapabilities(CaptureEngine aCapEngine, 180 const char* deviceUniqueIdUTF8); 181 int ReleaseCapture(CaptureEngine aCapEngine, const int capture_id); 182 int StartCapture(CaptureEngine aCapEngine, const int capture_id, 183 const webrtc::VideoCaptureCapability& capability, 184 const NormalizedConstraints& constraints, 185 const dom::VideoResizeModeEnum& resize_mode, 186 FrameRelay* func); 187 int FocusOnSelectedSource(CaptureEngine aCapEngine, const int capture_id); 188 int StopCapture(CaptureEngine aCapEngine, const int capture_id); 189 // Returns a non-negative capture identifier or -1 on failure. 190 int AllocateCapture(CaptureEngine aCapEngine, const char* unique_idUTF8, 191 uint64_t aWindowID); 192 int GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8, 193 const unsigned int capability_number, 194 webrtc::VideoCaptureCapability* capability); 195 int GetCaptureDevice(CaptureEngine aCapEngine, unsigned int list_number, 196 char* device_nameUTF8, 197 const unsigned int device_nameUTF8Length, 198 char* unique_idUTF8, 199 const unsigned int unique_idUTF8Length, bool* scary); 200 int EnsureInitialized(CaptureEngine aCapEngine); 201 202 template <typename This> 203 int ConnectDeviceListChangeListener(MediaEventListener* aListener, 204 AbstractThread* aTarget, This* aThis, 205 void (This::*aMethod)()) { 206 // According to the spec, if the script sets 207 // navigator.mediaDevices.ondevicechange and the permission state is 208 // "always granted", the User Agent MUST fires a devicechange event when 209 // a new media input device is made available, even the script never 210 // call getusermedia or enumerateDevices. 211 212 // In order to detect the event, we need to init the camera engine. 213 // Currently EnsureInitialized(aCapEngine) is only called when one of 214 // CamerasParent api, e.g., RecvNumberOfCaptureDevices(), is called. 215 216 // So here we setup camera engine via EnsureInitialized(aCapEngine) 217 218 EnsureInitialized(CameraEngine); 219 *aListener = mDeviceListChangeEvent.Connect(aTarget, aThis, aMethod); 220 return IPC_OK(); 221 } 222 223 FrameRelay* Callback(int capture_id); 224 225 private: 226 CamerasChild(); 227 ~CamerasChild(); 228 // Dispatch a Runnable to the PCamerasParent, by executing it on the 229 // decidecated Cameras IPC/PBackground thread. 230 enum class DispatchToParentResult : int8_t { 231 SUCCESS = 0, 232 FAILURE = -1, 233 DISCONNECTED = -2, 234 }; 235 DispatchToParentResult DispatchToParent(nsIRunnable* aRunnable, 236 MonitorAutoLock& aMonitor); 237 void AddCallback(int capture_id, FrameRelay* render); 238 void RemoveCallback(int capture_id); 239 240 nsTArray<CapturerElement> mCallbacks; 241 // Protects the callback arrays 242 Mutex mCallbackMutex MOZ_UNANNOTATED; 243 244 bool mIPCIsAlive; 245 246 // Hold to prevent multiple outstanding requests. We don't use 247 // request IDs so we only support one at a time. Don't want try 248 // to use the webrtc.org API from multiple threads simultanously. 249 // The monitor below isn't sufficient for this, as it will drop 250 // the lock when Wait-ing for a response, allowing us to send a new 251 // request. The Notify on receiving the response will then unblock 252 // both waiters and one will be guaranteed to get the wrong result. 253 // Take this one before taking mReplyMonitor. 254 Mutex mRequestMutex MOZ_UNANNOTATED; 255 // Hold to wait for an async response to our calls *and* until the 256 // user of LockAndDispatch<> has read the data out. This is done by 257 // keeping the LockAndDispatch object alive. 258 Monitor mReplyMonitor MOZ_UNANNOTATED; 259 // Async response valid? 260 bool mReceivedReply; 261 // Async responses data contents; 262 bool mReplySuccess; 263 int mReplyInteger; 264 webrtc::VideoCaptureCapability* mReplyCapability = nullptr; 265 nsCString mReplyDeviceName; 266 nsCString mReplyDeviceID; 267 bool mReplyScary; 268 MediaEventProducer<void> mDeviceListChangeEvent; 269 }; 270 271 } // namespace camera 272 } // namespace mozilla 273 274 #endif // mozilla_CamerasChild_h