Bug 1239384 - Remove static interface for Cameras using forwarding. r=jesup

This commit is contained in:
Gian-Carlo Pascutto 2016-01-18 17:15:05 +01:00
parent e6a4fbc47c
commit a621015f0b
4 changed files with 161 additions and 242 deletions

View File

@ -29,74 +29,16 @@ mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
namespace mozilla {
namespace camera {
// 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.
CamerasSingleton::CamerasSingleton()
: mCamerasMutex("CamerasSingleton::mCamerasMutex"),
mCameras(nullptr),
mCamerasChildThread(nullptr) {
LOG(("CamerasSingleton: %p", this));
}
// 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()
: mCamerasMutex("CamerasSingleton::mCamerasMutex"),
mCameras(nullptr),
mCamerasChildThread(nullptr) {
LOG(("CamerasSingleton: %p", this));
}
~CamerasSingleton() {
LOG(("~CamerasSingleton: %p", this));
}
static CamerasSingleton& GetInstance() {
static CamerasSingleton instance;
return instance;
}
static OffTheBooksMutex& Mutex() {
return GetInstance().mCamerasMutex;
}
static CamerasChild*& Child() {
GetInstance().Mutex().AssertCurrentThreadOwns();
return GetInstance().mCameras;
}
static nsCOMPtr<nsIThread>& Thread() {
GetInstance().Mutex().AssertCurrentThreadOwns();
return GetInstance().mCamerasChildThread;
}
private:
// 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;
};
CamerasSingleton::~CamerasSingleton() {
LOG(("~CamerasSingleton: %p", this));
}
class InitializeIPCThread : public nsRunnable
{
@ -135,7 +77,7 @@ private:
CamerasChild* mCamerasChild;
};
static CamerasChild*
CamerasChild*
GetCamerasChild() {
CamerasSingleton::Mutex().AssertCurrentThreadOwns();
if (!CamerasSingleton::Child()) {
@ -189,17 +131,6 @@ CamerasChild::RecvReplySuccess(void)
return true;
}
int NumberOfCapabilities(CaptureEngine aCapEngine, const char* deviceUniqueIdUTF8)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->NumberOfCapabilities(aCapEngine, deviceUniqueIdUTF8);
} else {
return 0;
}
}
bool
CamerasChild::RecvReplyNumberOfCapabilities(const int& numdev)
{
@ -263,17 +194,6 @@ CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
return mReplyInteger;
}
int NumberOfCaptureDevices(CaptureEngine aCapEngine)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->NumberOfCaptureDevices(aCapEngine);
} else {
return 0;
}
}
int
CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine)
{
@ -307,22 +227,6 @@ CamerasChild::RecvReplyNumberOfCaptureDevices(const int& numdev)
return true;
}
int GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8,
const unsigned int capability_number,
webrtc::CaptureCapability& capability)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->GetCaptureCapability(aCapEngine,
unique_idUTF8,
capability_number,
capability);
} else {
return -1;
}
}
int
CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
const char* unique_idUTF8,
@ -365,27 +269,6 @@ CamerasChild::RecvReplyGetCaptureCapability(const CaptureCapability& ipcCapabili
return true;
}
int GetCaptureDevice(CaptureEngine aCapEngine,
unsigned int list_number, char* device_nameUTF8,
const unsigned int device_nameUTF8Length,
char* unique_idUTF8,
const unsigned int unique_idUTF8Length)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->GetCaptureDevice(aCapEngine,
list_number,
device_nameUTF8,
device_nameUTF8Length,
unique_idUTF8,
unique_idUTF8Length);
} else {
return -1;
}
}
int
CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
unsigned int list_number, char* device_nameUTF8,
@ -427,23 +310,6 @@ CamerasChild::RecvReplyGetCaptureDevice(const nsCString& device_name,
return true;
}
int AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->AllocateCaptureDevice(aCapEngine,
unique_idUTF8,
unique_idUTF8Length,
capture_id);
} else {
return -1;
}
}
int
CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
@ -483,17 +349,6 @@ CamerasChild::RecvReplyAllocateCaptureDevice(const int& numdev)
return true;
}
int ReleaseCaptureDevice(CaptureEngine aCapEngine, const int capture_id)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->ReleaseCaptureDevice(aCapEngine, capture_id);
} else {
return -1;
}
}
int
CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
const int capture_id)
@ -539,23 +394,6 @@ CamerasChild::RemoveCallback(const CaptureEngine aCapEngine, const int capture_i
}
}
int StartCapture(CaptureEngine aCapEngine,
const int capture_id,
webrtc::CaptureCapability& webrtcCaps,
webrtc::ExternalRenderer* cb)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->StartCapture(aCapEngine,
capture_id,
webrtcCaps,
cb);
} else {
return -1;
}
}
int
CamerasChild::StartCapture(CaptureEngine aCapEngine,
const int capture_id,
@ -586,17 +424,6 @@ CamerasChild::StartCapture(CaptureEngine aCapEngine,
return 0;
}
int StopCapture(CaptureEngine aCapEngine, const int capture_id)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->StopCapture(aCapEngine, capture_id);
} else {
return -1;
}
}
int
CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
{

View File

@ -7,6 +7,7 @@
#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"
@ -46,33 +47,96 @@ struct CapturerElement {
webrtc::ExternalRenderer* callback;
};
// statically mirror webrtc.org ViECapture API
// these are called via MediaManager->MediaEngineRemoteVideoSource
// on the MediaManager thread
int NumberOfCapabilities(CaptureEngine aCapEngine,
const char* deviceUniqueIdUTF8);
int GetCaptureCapability(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int capability_number,
webrtc::CaptureCapability& capability);
int NumberOfCaptureDevices(CaptureEngine aCapEngine);
int GetCaptureDevice(CaptureEngine aCapEngine,
unsigned int list_number, char* device_nameUTF8,
const unsigned int device_nameUTF8Length,
char* unique_idUTF8,
const unsigned int unique_idUTF8Length);
int AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id);
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);
// Forward declaration so we can work with pointers to it.
class CamerasChild;
// 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 GetInstance().mCamerasMutex;
}
static CamerasChild*& Child() {
Mutex().AssertCurrentThreadOwns();
return GetInstance().mCameras;
}
static nsCOMPtr<nsIThread>& Thread() {
Mutex().AssertCurrentThreadOwns();
return GetInstance().mCamerasChildThread;
}
private:
static CamerasSingleton& GetInstance() {
static CamerasSingleton instance;
return instance;
}
// 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();
// 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
{
friend class mozilla::ipc::BackgroundChildImpl;

View File

@ -17,6 +17,12 @@ extern mozilla::LogModule* GetMediaManagerLog();
namespace mozilla {
// These need a definition somewhere because template
// code is allowed to take their address, and they aren't
// guaranteed to have one without this.
const unsigned int MediaEngineSource::kMaxDeviceNameLength;
const unsigned int MediaEngineSource::kMaxUniqueIdLength;;
using dom::ConstrainLongRange;
NS_IMPL_ISUPPORTS0(MediaEngineRemoteVideoSource)
@ -38,10 +44,11 @@ MediaEngineRemoteVideoSource::Init()
LOG((__PRETTY_FUNCTION__));
char deviceName[kMaxDeviceNameLength];
char uniqueId[kMaxUniqueIdLength];
if (mozilla::camera::GetCaptureDevice(mCapEngine,
mCaptureIndex,
deviceName, kMaxDeviceNameLength,
uniqueId, kMaxUniqueIdLength)) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureDevice,
mCapEngine, mCaptureIndex,
deviceName, kMaxDeviceNameLength,
uniqueId, kMaxUniqueIdLength)) {
LOG(("Error initializing RemoteVideoSource (GetCaptureDevice)"));
return;
}
@ -111,9 +118,9 @@ MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstr
return NS_ERROR_UNEXPECTED;
}
if (mozilla::camera::AllocateCaptureDevice(mCapEngine,
GetUUID().get(),
kMaxUniqueIdLength, mCaptureIndex)) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::AllocateCaptureDevice,
mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex)) {
return NS_ERROR_FAILURE;
}
mState = kAllocated;
@ -145,7 +152,9 @@ MediaEngineRemoteVideoSource::Deallocate()
if (mState != kStopped && mState != kAllocated) {
return NS_ERROR_FAILURE;
}
mozilla::camera::ReleaseCaptureDevice(mCapEngine, mCaptureIndex);
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::ReleaseCaptureDevice,
mCapEngine, mCaptureIndex);
mState = kReleased;
LOG(("Video device %d deallocated", mCaptureIndex));
} else {
@ -179,8 +188,9 @@ MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
mState = kStarted;
mTrackID = aID;
if (mozilla::camera::StartCapture(mCapEngine,
mCaptureIndex, mCapability, this)) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StartCapture,
mCapEngine, mCaptureIndex, mCapability, this)) {
LOG(("StartCapture failed"));
return NS_ERROR_FAILURE;
}
@ -217,7 +227,9 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
mImage = nullptr;
}
mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StopCapture,
mCapEngine, mCaptureIndex);
return NS_OK;
}
@ -239,9 +251,12 @@ MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstra
return NS_OK;
}
mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
if (mozilla::camera::StartCapture(mCapEngine,
mCaptureIndex, mCapability, this)) {
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StopCapture,
mCapEngine, mCaptureIndex);
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StartCapture,
mCapEngine, mCaptureIndex, mCapability, this)) {
LOG(("StartCapture failed"));
return NS_ERROR_FAILURE;
}
@ -354,7 +369,10 @@ MediaEngineRemoteVideoSource::DeliverFrame(unsigned char* buffer,
size_t
MediaEngineRemoteVideoSource::NumCapabilities()
{
int num = mozilla::camera::NumberOfCapabilities(mCapEngine, GetUUID().get());
int num = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCapabilities,
mCapEngine,
GetUUID().get());
if (num > 0) {
return num;
}
@ -433,10 +451,12 @@ MediaEngineRemoteVideoSource::GetCapability(size_t aIndex,
if (!mHardcodedCapabilities.IsEmpty()) {
MediaEngineCameraVideoSource::GetCapability(aIndex, aOut);
}
mozilla::camera::GetCaptureCapability(mCapEngine,
GetUUID().get(),
aIndex,
aOut);
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureCapability,
mCapEngine,
GetUUID().get(),
aIndex,
aOut);
}
void MediaEngineRemoteVideoSource::Refresh(int aIndex) {
@ -446,10 +466,11 @@ void MediaEngineRemoteVideoSource::Refresh(int aIndex) {
char deviceName[kMaxDeviceNameLength];
char uniqueId[kMaxUniqueIdLength];
if (mozilla::camera::GetCaptureDevice(mCapEngine,
aIndex,
deviceName, sizeof(deviceName),
uniqueId, sizeof(uniqueId))) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureDevice,
mCapEngine, aIndex,
deviceName, sizeof(deviceName),
uniqueId, sizeof(uniqueId))) {
return;
}

View File

@ -161,7 +161,9 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
* mVideoSources must be updated.
*/
int num;
num = mozilla::camera::NumberOfCaptureDevices(capEngine);
num = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCaptureDevices,
capEngine);
if (num <= 0) {
return;
}
@ -175,11 +177,12 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
uniqueId[0] = '\0';
int error;
error = mozilla::camera::GetCaptureDevice(capEngine,
i, deviceName,
sizeof(deviceName), uniqueId,
sizeof(uniqueId));
error = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureDevice,
capEngine,
i, deviceName,
sizeof(deviceName), uniqueId,
sizeof(uniqueId));
if (error) {
LOG(("camera:GetCaptureDevice: Failed %d", error ));
continue;
@ -188,13 +191,17 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
LOG((" Capture Device Index %d, Name %s", i, deviceName));
webrtc::CaptureCapability cap;
int numCaps = mozilla::camera::NumberOfCapabilities(capEngine,
uniqueId);
int numCaps = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCapabilities,
capEngine,
uniqueId);
LOG(("Number of Capabilities %d", numCaps));
for (int j = 0; j < numCaps; j++) {
if (mozilla::camera::GetCaptureCapability(capEngine,
uniqueId,
j, cap ) != 0 ) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureCapability,
capEngine,
uniqueId,
j, cap) != 0) {
break;
}
LOG(("type=%d width=%d height=%d maxFPS=%d",