tor-browser

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

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