Mypal/dom/media/systemservices/CamerasChild.h

259 lines
9.9 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "mozilla/Move.h"
#include "mozilla/Pair.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/camera/PCamerasChild.h"
#include "mozilla/camera/PCamerasParent.h"
#include "mozilla/media/DeviceChangeCallback.h"
#include "mozilla/Mutex.h"
#include "base/singleton.h"
#include "nsCOMPtr.h"
// conflicts with #include of scoped_ptr.h
#undef FF
#include "webrtc/common.h"
// Video Engine
#include "webrtc/video_engine/include/vie_base.h"
#include "webrtc/video_engine/include/vie_capture.h"
#include "webrtc/video_engine/include/vie_render.h"
namespace mozilla {
namespace ipc {
class BackgroundChildImpl;
}
namespace camera {
struct CapturerElement {
CaptureEngine engine;
int id;
webrtc::ExternalRenderer* 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:
CamerasSingleton();
~CamerasSingleton();
static OffTheBooksMutex& Mutex() {
return gTheInstance.get()->mCamerasMutex;
}
static CamerasChild*& Child() {
Mutex().AssertCurrentThreadOwns();
return gTheInstance.get()->mCameras;
}
static nsCOMPtr<nsIThread>& Thread() {
Mutex().AssertCurrentThreadOwns();
return gTheInstance.get()->mCamerasChildThread;
}
static nsCOMPtr<nsIThread>& FakeDeviceChangeEventThread() {
Mutex().AssertCurrentThreadOwns();
return gTheInstance.get()->mFakeDeviceChangeEventThread;
}
static bool InShutdown() {
return gTheInstance.get()->mInShutdown;
}
static void StartShutdown() {
gTheInstance.get()->mInShutdown = true;
}
private:
static Singleton<CamerasSingleton> gTheInstance;
// 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;
nsCOMPtr<nsIThread> mFakeDeviceChangeEventThread;
Atomic<bool> mInShutdown;
};
// 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)(mozilla::Forward<ARGS>(args)...);
} else {
return -1;
}
}
class CamerasChild final : public PCamerasChild
,public DeviceChangeCallback
{
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
virtual bool RecvDeliverFrame(const CaptureEngine&, const int&, mozilla::ipc::Shmem&&,
const size_t&, const uint32_t&, const int64_t&,
const int64_t&) override;
virtual bool RecvFrameSizeChange(const CaptureEngine&, const int&,
const int& w, const int& h) override;
virtual bool RecvDeviceChange() override;
virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
int SetFakeDeviceChangeEvents();
// these are response messages to our outgoing requests
virtual bool RecvReplyNumberOfCaptureDevices(const int&) override;
virtual bool RecvReplyNumberOfCapabilities(const int&) override;
virtual bool RecvReplyAllocateCaptureDevice(const int&) override;
virtual bool RecvReplyGetCaptureCapability(const CaptureCapability& capability) override;
virtual bool RecvReplyGetCaptureDevice(const nsCString& device_name,
const nsCString& device_id,
const bool& scary) override;
virtual bool RecvReplyFailure(void) override;
virtual bool RecvReplySuccess(void) override;
virtual 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 ReleaseCaptureDevice(CaptureEngine aCapEngine,
const int capture_id);
int StartCapture(CaptureEngine aCapEngine,
const int capture_id, webrtc::CaptureCapability& capability,
webrtc::ExternalRenderer* func);
int StopCapture(CaptureEngine aCapEngine, const int capture_id);
int AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id,
const nsACString& aOrigin);
int GetCaptureCapability(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int capability_number,
webrtc::CaptureCapability& 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);
void ShutdownAll();
int EnsureInitialized(CaptureEngine aCapEngine);
webrtc::ExternalRenderer* 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,
webrtc::ExternalRenderer* render);
void RemoveCallback(const CaptureEngine aCapEngine, const int capture_id);
void ShutdownParent();
void ShutdownChild();
nsTArray<CapturerElement> mCallbacks;
// Protects the callback arrays
Mutex mCallbackMutex;
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;
// Hold to wait for an async response to our calls
Monitor mReplyMonitor;
// Async response valid?
bool mReceivedReply;
// Async responses data contents;
bool mReplySuccess;
int mReplyInteger;
webrtc::CaptureCapability mReplyCapability;
nsCString mReplyDeviceName;
nsCString mReplyDeviceID;
bool mReplyScary;
};
} // namespace camera
} // namespace mozilla
#endif // mozilla_CamerasChild_h