summaryrefslogtreecommitdiffstats
path: root/dom/media/systemservices
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/systemservices')
-rw-r--r--dom/media/systemservices/CamerasChild.cpp724
-rw-r--r--dom/media/systemservices/CamerasChild.h250
-rw-r--r--dom/media/systemservices/CamerasParent.cpp1094
-rw-r--r--dom/media/systemservices/CamerasParent.h187
-rw-r--r--dom/media/systemservices/CamerasTypes.h38
-rw-r--r--dom/media/systemservices/DeviceChangeCallback.h61
-rw-r--r--dom/media/systemservices/LoadManager.cpp227
-rw-r--r--dom/media/systemservices/LoadManager.h111
-rw-r--r--dom/media/systemservices/LoadManagerFactory.cpp52
-rw-r--r--dom/media/systemservices/LoadManagerFactory.h18
-rw-r--r--dom/media/systemservices/LoadMonitor.cpp658
-rw-r--r--dom/media/systemservices/LoadMonitor.h61
-rw-r--r--dom/media/systemservices/MediaChild.cpp115
-rw-r--r--dom/media/systemservices/MediaChild.h55
-rw-r--r--dom/media/systemservices/MediaParent.cpp516
-rw-r--r--dom/media/systemservices/MediaParent.h77
-rw-r--r--dom/media/systemservices/MediaSystemResourceClient.cpp75
-rw-r--r--dom/media/systemservices/MediaSystemResourceClient.h94
-rw-r--r--dom/media/systemservices/MediaSystemResourceManager.cpp387
-rw-r--r--dom/media/systemservices/MediaSystemResourceManager.h82
-rw-r--r--dom/media/systemservices/MediaSystemResourceManagerChild.cpp53
-rw-r--r--dom/media/systemservices/MediaSystemResourceManagerChild.h66
-rw-r--r--dom/media/systemservices/MediaSystemResourceManagerParent.cpp77
-rw-r--r--dom/media/systemservices/MediaSystemResourceManagerParent.h57
-rw-r--r--dom/media/systemservices/MediaSystemResourceMessageUtils.h25
-rw-r--r--dom/media/systemservices/MediaSystemResourceService.cpp256
-rw-r--r--dom/media/systemservices/MediaSystemResourceService.h89
-rw-r--r--dom/media/systemservices/MediaSystemResourceTypes.h23
-rw-r--r--dom/media/systemservices/MediaTaskUtils.h50
-rw-r--r--dom/media/systemservices/MediaUtils.cpp15
-rw-r--r--dom/media/systemservices/MediaUtils.h373
-rw-r--r--dom/media/systemservices/OSXRunLoopSingleton.cpp44
-rw-r--r--dom/media/systemservices/OSXRunLoopSingleton.h25
-rw-r--r--dom/media/systemservices/OpenSLESProvider.cpp189
-rw-r--r--dom/media/systemservices/OpenSLESProvider.h68
-rw-r--r--dom/media/systemservices/PCameras.ipdl65
-rw-r--r--dom/media/systemservices/PMedia.ipdl51
-rw-r--r--dom/media/systemservices/PMediaSystemResourceManager.ipdl37
-rw-r--r--dom/media/systemservices/ShmemPool.cpp82
-rw-r--r--dom/media/systemservices/ShmemPool.h153
-rw-r--r--dom/media/systemservices/moz.build104
41 files changed, 6784 insertions, 0 deletions
diff --git a/dom/media/systemservices/CamerasChild.cpp b/dom/media/systemservices/CamerasChild.cpp
new file mode 100644
index 000000000..0f7d1c1df
--- /dev/null
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -0,0 +1,724 @@
+/* -*- 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/. */
+
+#include "CamerasChild.h"
+
+#include "webrtc/video_engine/include/vie_capture.h"
+#undef FF
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/Logging.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/Unused.h"
+#include "MediaUtils.h"
+#include "nsThreadUtils.h"
+
+#undef LOG
+#undef LOG_ENABLED
+mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
+#define LOG(args) MOZ_LOG(gCamerasChildLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasChildLog, mozilla::LogLevel::Debug)
+
+#define FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS 5000
+#define FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT 30
+
+namespace mozilla {
+namespace camera {
+
+CamerasSingleton::CamerasSingleton()
+ : mCamerasMutex("CamerasSingleton::mCamerasMutex"),
+ mCameras(nullptr),
+ mCamerasChildThread(nullptr),
+ mFakeDeviceChangeEventThread(nullptr) {
+ LOG(("CamerasSingleton: %p", this));
+}
+
+CamerasSingleton::~CamerasSingleton() {
+ LOG(("~CamerasSingleton: %p", this));
+}
+
+class FakeOnDeviceChangeEventRunnable : public Runnable
+{
+public:
+ explicit FakeOnDeviceChangeEventRunnable(uint8_t counter)
+ : mCounter(counter) {}
+
+ NS_IMETHOD Run() override
+ {
+ OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+
+ CamerasChild* child = CamerasSingleton::Child();
+ if (child) {
+ child->OnDeviceChange();
+
+ if (mCounter++ < FAKE_ONDEVICECHANGE_EVENT_REPEAT_COUNT) {
+ RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(mCounter);
+ CamerasSingleton::FakeDeviceChangeEventThread()->DelayedDispatch(evt.forget(),
+ FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS);
+ }
+ }
+
+ return NS_OK;
+ }
+
+private:
+ uint8_t mCounter;
+};
+
+class InitializeIPCThread : public Runnable
+{
+public:
+ InitializeIPCThread()
+ : mCamerasChild(nullptr) {}
+
+ NS_IMETHOD Run() override {
+ // Try to get the PBackground handle
+ ipc::PBackgroundChild* existingBackgroundChild =
+ ipc::BackgroundChild::GetForCurrentThread();
+ // If it's not spun up yet, block until it is, and retry
+ if (!existingBackgroundChild) {
+ LOG(("No existingBackgroundChild"));
+ existingBackgroundChild =
+ ipc::BackgroundChild::SynchronouslyCreateForCurrentThread();
+ LOG(("BackgroundChild: %p", existingBackgroundChild));
+ if (!existingBackgroundChild) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // Create CamerasChild
+ // We will be returning the resulting pointer (synchronously) to our caller.
+ mCamerasChild =
+ static_cast<mozilla::camera::CamerasChild*>(existingBackgroundChild->SendPCamerasConstructor());
+
+ return NS_OK;
+ }
+
+ CamerasChild* GetCamerasChild() {
+ return mCamerasChild;
+ }
+
+private:
+ CamerasChild* mCamerasChild;
+};
+
+CamerasChild*
+GetCamerasChild() {
+ CamerasSingleton::Mutex().AssertCurrentThreadOwns();
+ if (!CamerasSingleton::Child()) {
+ MOZ_ASSERT(!NS_IsMainThread(), "Should not be on the main Thread");
+ MOZ_ASSERT(!CamerasSingleton::Thread());
+ LOG(("No sCameras, setting up IPC Thread"));
+ nsresult rv = NS_NewNamedThread("Cameras IPC",
+ getter_AddRefs(CamerasSingleton::Thread()));
+ if (NS_FAILED(rv)) {
+ LOG(("Error launching IPC Thread"));
+ return nullptr;
+ }
+
+ // At this point we are in the MediaManager thread, and the thread we are
+ // dispatching to is the specific Cameras IPC thread that was just made
+ // above, so now we will fire off a runnable to run
+ // BackgroundChild::SynchronouslyCreateForCurrentThread there, while we
+ // block in this thread.
+ // We block until the following happens in the Cameras IPC thread:
+ // 1) Creation of PBackground finishes
+ // 2) Creation of PCameras finishes by sending a message to the parent
+ RefPtr<InitializeIPCThread> runnable = new InitializeIPCThread();
+ RefPtr<SyncRunnable> sr = new SyncRunnable(runnable);
+ sr->DispatchToThread(CamerasSingleton::Thread());
+ CamerasSingleton::Child() = runnable->GetCamerasChild();
+ }
+ if (!CamerasSingleton::Child()) {
+ LOG(("Failed to set up CamerasChild, are we in shutdown?"));
+ }
+ return CamerasSingleton::Child();
+}
+
+CamerasChild*
+GetCamerasChildIfExists() {
+ OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+ return CamerasSingleton::Child();
+}
+
+int CamerasChild::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
+{
+ // 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
+ // CamerasaParent api, e.g., RecvNumberOfCaptureDevices(), is called.
+
+ // So here we setup camera engine via EnsureInitialized(aCapEngine)
+
+ EnsureInitialized(CameraEngine);
+ return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
+}
+
+bool
+CamerasChild::RecvReplyFailure(void)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = false;
+ monitor.Notify();
+ return true;
+}
+
+bool
+CamerasChild::RecvReplySuccess(void)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = true;
+ monitor.Notify();
+ return true;
+}
+
+bool
+CamerasChild::RecvReplyNumberOfCapabilities(const int& numdev)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = true;
+ mReplyInteger = numdev;
+ monitor.Notify();
+ return true;
+}
+
+// Helper function to dispatch calls to the IPC Thread and
+// CamerasChild object. Takes the needed locks and dispatches.
+// Takes a "failed" value and a reference to the output variable
+// as parameters, will return the right one depending on whether
+// dispatching succeeded.
+template <class T = int>
+class LockAndDispatch
+{
+public:
+ LockAndDispatch(CamerasChild* aCamerasChild,
+ const char* aRequestingFunc,
+ nsIRunnable *aRunnable,
+ const T& aFailureValue = T(-1), const T& aSuccessValue = T(0))
+ : mCamerasChild(aCamerasChild), mRequestingFunc(aRequestingFunc),
+ mRunnable(aRunnable),
+ mReplyLock(aCamerasChild->mReplyMonitor),
+ mRequestLock(aCamerasChild->mRequestMutex),
+ mSuccess(true),
+ mFailureValue(aFailureValue), mSuccessValue(aSuccessValue)
+ {
+ Dispatch();
+ }
+
+ const T& ReturnValue() const {
+ if (mSuccess) {
+ return mSuccessValue;
+ } else {
+ return mFailureValue;
+ }
+ }
+
+ const bool& Success() const {
+ return mSuccess;
+ }
+
+private:
+ void Dispatch() {
+ if (!mCamerasChild->DispatchToParent(mRunnable, mReplyLock)) {
+ LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc));
+ mSuccess = false;
+ }
+ }
+
+ CamerasChild* mCamerasChild;
+ const char* mRequestingFunc;
+ nsIRunnable* mRunnable;
+ // Prevent concurrent use of the reply variables by holding
+ // the mReplyMonitor. Note that this is unlocked while waiting for
+ // the reply to be filled in, necessitating the additional mRequestLock/Mutex;
+ MonitorAutoLock mReplyLock;
+ MutexAutoLock mRequestLock;
+ bool mSuccess;
+ const T& mFailureValue;
+ const T& mSuccessValue;
+};
+
+bool
+CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
+ MonitorAutoLock& aMonitor)
+{
+ CamerasSingleton::Mutex().AssertCurrentThreadOwns();
+ CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+ // We can't see if the send worked, so we need to be able to bail
+ // out on shutdown (when it failed and we won't get a reply).
+ if (!mIPCIsAlive) {
+ return false;
+ }
+ // Guard against spurious wakeups.
+ mReceivedReply = false;
+ // Wait for a reply
+ do {
+ aMonitor.Wait();
+ } while (!mReceivedReply && mIPCIsAlive);
+ if (!mReplySuccess) {
+ return false;
+ }
+ return true;
+}
+
+int
+CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
+ const char* deviceUniqueIdUTF8)
+{
+ LOG((__PRETTY_FUNCTION__));
+ LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8));
+ nsCString unique_id(deviceUniqueIdUTF8);
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString>
+ (this, &CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
+ LOG(("Capture capability count: %d", dispatcher.ReturnValue()));
+ return dispatcher.ReturnValue();
+}
+
+int
+CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine)
+{
+ LOG((__PRETTY_FUNCTION__));
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine>
+ (this, &CamerasChild::SendNumberOfCaptureDevices, aCapEngine);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
+ LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
+ return dispatcher.ReturnValue();
+}
+
+bool
+CamerasChild::RecvReplyNumberOfCaptureDevices(const int& numdev)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = true;
+ mReplyInteger = numdev;
+ monitor.Notify();
+ return true;
+}
+
+int
+CamerasChild::EnsureInitialized(CaptureEngine aCapEngine)
+{
+ LOG((__PRETTY_FUNCTION__));
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine>
+ (this, &CamerasChild::SendEnsureInitialized, aCapEngine);
+ LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger);
+ LOG(("Capture Devices: %d", dispatcher.ReturnValue()));
+ return dispatcher.ReturnValue();
+}
+
+int
+CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
+ const char* unique_idUTF8,
+ const unsigned int capability_number,
+ webrtc::CaptureCapability& capability)
+{
+ LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number));
+ nsCString unique_id(unique_idUTF8);
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, unsigned int>
+ (this, &CamerasChild::SendGetCaptureCapability, aCapEngine, unique_id, capability_number);
+ LockAndDispatch<> dispatcher(this, __func__, runnable);
+ if (dispatcher.Success()) {
+ capability = mReplyCapability;
+ }
+ return dispatcher.ReturnValue();
+}
+
+bool
+CamerasChild::RecvReplyGetCaptureCapability(const CaptureCapability& ipcCapability)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = true;
+ mReplyCapability.width = ipcCapability.width();
+ mReplyCapability.height = ipcCapability.height();
+ mReplyCapability.maxFPS = ipcCapability.maxFPS();
+ mReplyCapability.expectedCaptureDelay = ipcCapability.expectedCaptureDelay();
+ mReplyCapability.rawType = static_cast<webrtc::RawVideoType>(ipcCapability.rawType());
+ mReplyCapability.codecType = static_cast<webrtc::VideoCodecType>(ipcCapability.codecType());
+ mReplyCapability.interlaced = ipcCapability.interlaced();
+ monitor.Notify();
+ return true;
+}
+
+int
+CamerasChild::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)
+{
+ LOG((__PRETTY_FUNCTION__));
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, unsigned int>
+ (this, &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number);
+ LockAndDispatch<> dispatcher(this, __func__, runnable);
+ if (dispatcher.Success()) {
+ base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length);
+ base::strlcpy(unique_idUTF8, mReplyDeviceID.get(), unique_idUTF8Length);
+ if (scary) {
+ *scary = mReplyScary;
+ }
+ LOG(("Got %s name %s id", device_nameUTF8, unique_idUTF8));
+ }
+ return dispatcher.ReturnValue();
+}
+
+bool
+CamerasChild::RecvReplyGetCaptureDevice(const nsCString& device_name,
+ const nsCString& device_id,
+ const bool& scary)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = true;
+ mReplyDeviceName = device_name;
+ mReplyDeviceID = device_id;
+ mReplyScary = scary;
+ monitor.Notify();
+ return true;
+}
+
+int
+CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
+ const char* unique_idUTF8,
+ const unsigned int unique_idUTF8Length,
+ int& capture_id,
+ const nsACString& aOrigin)
+{
+ LOG((__PRETTY_FUNCTION__));
+ nsCString unique_id(unique_idUTF8);
+ nsCString origin(aOrigin);
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, nsCString, nsCString>
+ (this, &CamerasChild::SendAllocateCaptureDevice, aCapEngine, unique_id, origin);
+ LockAndDispatch<> dispatcher(this, __func__, runnable);
+ if (dispatcher.Success()) {
+ LOG(("Capture Device allocated: %d", mReplyInteger));
+ capture_id = mReplyInteger;
+ }
+ return dispatcher.ReturnValue();
+}
+
+
+bool
+CamerasChild::RecvReplyAllocateCaptureDevice(const int& numdev)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MonitorAutoLock monitor(mReplyMonitor);
+ mReceivedReply = true;
+ mReplySuccess = true;
+ mReplyInteger = numdev;
+ monitor.Notify();
+ return true;
+}
+
+int
+CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
+ const int capture_id)
+{
+ LOG((__PRETTY_FUNCTION__));
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>
+ (this, &CamerasChild::SendReleaseCaptureDevice, aCapEngine, capture_id);
+ LockAndDispatch<> dispatcher(this, __func__, runnable);
+ return dispatcher.ReturnValue();
+}
+
+void
+CamerasChild::AddCallback(const CaptureEngine aCapEngine, const int capture_id,
+ webrtc::ExternalRenderer* render)
+{
+ MutexAutoLock lock(mCallbackMutex);
+ CapturerElement ce;
+ ce.engine = aCapEngine;
+ ce.id = capture_id;
+ ce.callback = render;
+ mCallbacks.AppendElement(ce);
+}
+
+void
+CamerasChild::RemoveCallback(const CaptureEngine aCapEngine, const int capture_id)
+{
+ MutexAutoLock lock(mCallbackMutex);
+ for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
+ CapturerElement ce = mCallbacks[i];
+ if (ce.engine == aCapEngine && ce.id == capture_id) {
+ mCallbacks.RemoveElementAt(i);
+ break;
+ }
+ }
+}
+
+int
+CamerasChild::StartCapture(CaptureEngine aCapEngine,
+ const int capture_id,
+ webrtc::CaptureCapability& webrtcCaps,
+ webrtc::ExternalRenderer* cb)
+{
+ LOG((__PRETTY_FUNCTION__));
+ AddCallback(aCapEngine, capture_id, cb);
+ CaptureCapability capCap(webrtcCaps.width,
+ webrtcCaps.height,
+ webrtcCaps.maxFPS,
+ webrtcCaps.expectedCaptureDelay,
+ webrtcCaps.rawType,
+ webrtcCaps.codecType,
+ webrtcCaps.interlaced);
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, int, CaptureCapability>
+ (this, &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap);
+ LockAndDispatch<> dispatcher(this, __func__, runnable);
+ return dispatcher.ReturnValue();
+}
+
+int
+CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
+{
+ LOG((__PRETTY_FUNCTION__));
+ nsCOMPtr<nsIRunnable> runnable =
+ mozilla::NewNonOwningRunnableMethod<CaptureEngine, int>
+ (this, &CamerasChild::SendStopCapture, aCapEngine, capture_id);
+ LockAndDispatch<> dispatcher(this, __func__, runnable);
+ if (dispatcher.Success()) {
+ RemoveCallback(aCapEngine, capture_id);
+ }
+ return dispatcher.ReturnValue();
+}
+
+void
+Shutdown(void)
+{
+ OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+ CamerasChild* child = CamerasSingleton::Child();
+ if (!child) {
+ // We don't want to cause everything to get fired up if we're
+ // really already shut down.
+ LOG(("Shutdown when already shut down"));
+ return;
+ }
+ child->ShutdownAll();
+}
+
+class ShutdownRunnable : public Runnable {
+public:
+ explicit
+ ShutdownRunnable(already_AddRefed<Runnable>&& aReplyEvent)
+ : mReplyEvent(aReplyEvent) {};
+
+ NS_IMETHOD Run() override {
+ LOG(("Closing BackgroundChild"));
+ ipc::BackgroundChild::CloseForCurrentThread();
+
+ NS_DispatchToMainThread(mReplyEvent.forget());
+
+ return NS_OK;
+ }
+
+private:
+ RefPtr<Runnable> mReplyEvent;
+};
+
+void
+CamerasChild::ShutdownAll()
+{
+ // Called with CamerasSingleton::Mutex() held
+ ShutdownParent();
+ ShutdownChild();
+}
+
+void
+CamerasChild::ShutdownParent()
+{
+ // Called with CamerasSingleton::Mutex() held
+ {
+ MonitorAutoLock monitor(mReplyMonitor);
+ mIPCIsAlive = false;
+ monitor.NotifyAll();
+ }
+ if (CamerasSingleton::Thread()) {
+ LOG(("Dispatching actor deletion"));
+ // Delete the parent actor.
+ // CamerasChild (this) will remain alive and is only deleted by the
+ // IPC layer when SendAllDone returns.
+ nsCOMPtr<nsIRunnable> deleteRunnable =
+ mozilla::NewNonOwningRunnableMethod(this, &CamerasChild::SendAllDone);
+ CamerasSingleton::Thread()->Dispatch(deleteRunnable, NS_DISPATCH_NORMAL);
+ } else {
+ LOG(("ShutdownParent called without PBackground thread"));
+ }
+}
+
+void
+CamerasChild::ShutdownChild()
+{
+ // Called with CamerasSingleton::Mutex() held
+ if (CamerasSingleton::Thread()) {
+ LOG(("PBackground thread exists, dispatching close"));
+ // Dispatch closing the IPC thread back to us when the
+ // BackgroundChild is closed.
+ RefPtr<ShutdownRunnable> runnable =
+ new ShutdownRunnable(NewRunnableMethod(CamerasSingleton::Thread(),
+ &nsIThread::Shutdown));
+ CamerasSingleton::Thread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+ } else {
+ LOG(("Shutdown called without PBackground thread"));
+ }
+ LOG(("Erasing sCameras & thread refs (original thread)"));
+ CamerasSingleton::Child() = nullptr;
+ CamerasSingleton::Thread() = nullptr;
+
+ if (CamerasSingleton::FakeDeviceChangeEventThread()) {
+ RefPtr<ShutdownRunnable> runnable =
+ new ShutdownRunnable(NewRunnableMethod(CamerasSingleton::FakeDeviceChangeEventThread(),
+ &nsIThread::Shutdown));
+ CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+ }
+ CamerasSingleton::FakeDeviceChangeEventThread() = nullptr;
+}
+
+bool
+CamerasChild::RecvDeliverFrame(const CaptureEngine& capEngine,
+ const int& capId,
+ mozilla::ipc::Shmem&& shmem,
+ const size_t& size,
+ const uint32_t& time_stamp,
+ const int64_t& ntp_time,
+ const int64_t& render_time)
+{
+ MutexAutoLock lock(mCallbackMutex);
+ if (Callback(capEngine, capId)) {
+ unsigned char* image = shmem.get<unsigned char>();
+ Callback(capEngine, capId)->DeliverFrame(image, size,
+ time_stamp,
+ ntp_time, render_time,
+ nullptr);
+ } else {
+ LOG(("DeliverFrame called with dead callback"));
+ }
+ SendReleaseFrame(shmem);
+ return true;
+}
+
+bool
+CamerasChild::RecvDeviceChange()
+{
+ this->OnDeviceChange();
+ return true;
+}
+
+int
+CamerasChild::SetFakeDeviceChangeEvents()
+{
+ CamerasSingleton::Mutex().AssertCurrentThreadOwns();
+
+ if(!CamerasSingleton::FakeDeviceChangeEventThread()) {
+ nsresult rv = NS_NewNamedThread("Fake DC Event",
+ getter_AddRefs(CamerasSingleton::FakeDeviceChangeEventThread()));
+ if (NS_FAILED(rv)) {
+ LOG(("Error launching Fake OnDeviceChange Event Thread"));
+ return -1;
+ }
+ }
+
+ // To simulate the devicechange event in mochitest,
+ // we fire a fake devicechange event in Camera IPC thread periodically
+ RefPtr<FakeOnDeviceChangeEventRunnable> evt = new FakeOnDeviceChangeEventRunnable(0);
+ CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(evt.forget(), NS_DISPATCH_NORMAL);
+
+ return 0;
+}
+
+bool
+CamerasChild::RecvFrameSizeChange(const CaptureEngine& capEngine,
+ const int& capId,
+ const int& w, const int& h)
+{
+ LOG((__PRETTY_FUNCTION__));
+ MutexAutoLock lock(mCallbackMutex);
+ if (Callback(capEngine, capId)) {
+ Callback(capEngine, capId)->FrameSizeChange(w, h, 0);
+ } else {
+ LOG(("Frame size change with dead callback"));
+ }
+ return true;
+}
+
+void
+CamerasChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ MonitorAutoLock monitor(mReplyMonitor);
+ mIPCIsAlive = false;
+ // Hopefully prevent us from getting stuck
+ // on replies that'll never come.
+ monitor.NotifyAll();
+}
+
+CamerasChild::CamerasChild()
+ : mCallbackMutex("mozilla::cameras::CamerasChild::mCallbackMutex"),
+ mIPCIsAlive(true),
+ mRequestMutex("mozilla::cameras::CamerasChild::mRequestMutex"),
+ mReplyMonitor("mozilla::cameras::CamerasChild::mReplyMonitor")
+{
+ LOG(("CamerasChild: %p", this));
+
+ MOZ_COUNT_CTOR(CamerasChild);
+}
+
+CamerasChild::~CamerasChild()
+{
+ LOG(("~CamerasChild: %p", this));
+
+ {
+ OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+ // In normal circumstances we've already shut down and the
+ // following does nothing. But on fatal IPC errors we will
+ // get destructed immediately, and should not try to reach
+ // the parent.
+ ShutdownChild();
+ }
+
+ MOZ_COUNT_DTOR(CamerasChild);
+}
+
+webrtc::ExternalRenderer* CamerasChild::Callback(CaptureEngine aCapEngine,
+ int capture_id)
+{
+ for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
+ CapturerElement ce = mCallbacks[i];
+ if (ce.engine == aCapEngine && ce.id == capture_id) {
+ return ce.callback;
+ }
+ }
+
+ return nullptr;
+}
+
+}
+}
diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h
new file mode 100644
index 000000000..1530714e9
--- /dev/null
+++ b/dom/media/systemservices/CamerasChild.h
@@ -0,0 +1,250 @@
+/* -*- 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 "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;
+ }
+
+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;
+};
+
+// 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_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
diff --git a/dom/media/systemservices/CamerasParent.cpp b/dom/media/systemservices/CamerasParent.cpp
new file mode 100644
index 000000000..808a076d7
--- /dev/null
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -0,0 +1,1094 @@
+/* -*- 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/. */
+
+#include "CamerasParent.h"
+#include "MediaEngine.h"
+#include "MediaUtils.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Services.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/Preferences.h"
+#include "nsIPermissionManager.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOM.h"
+#include "nsNetUtil.h"
+
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
+
+#if defined(_WIN32)
+#include <process.h>
+#define getpid() _getpid()
+#endif
+
+#undef LOG
+#undef LOG_ENABLED
+mozilla::LazyLogModule gCamerasParentLog("CamerasParent");
+#define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
+
+namespace mozilla {
+namespace camera {
+
+// 3 threads are involved in this code:
+// - the main thread for some setups, and occassionally for video capture setup
+// calls that don't work correctly elsewhere.
+// - the IPC thread on which PBackground is running and which receives and
+// sends messages
+// - a thread which will execute the actual (possibly slow) camera access
+// called "VideoCapture". On Windows this is a thread with an event loop
+// suitable for UI access.
+
+// InputObserver is owned by CamerasParent, and it has a ref to CamerasParent
+void InputObserver::DeviceChange() {
+ LOG((__PRETTY_FUNCTION__));
+ MOZ_ASSERT(mParent);
+
+ RefPtr<InputObserver> self(this);
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self]() -> nsresult {
+ if (self->mParent->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ Unused << self->mParent->SendDeviceChange();
+ return NS_OK;
+ });
+
+ nsIThread* thread = mParent->GetBackgroundThread();
+ MOZ_ASSERT(thread != nullptr);
+ thread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+};
+
+class FrameSizeChangeRunnable : public Runnable {
+public:
+ FrameSizeChangeRunnable(CamerasParent *aParent, CaptureEngine capEngine,
+ int cap_id, unsigned int aWidth, unsigned int aHeight)
+ : mParent(aParent), mCapEngine(capEngine), mCapId(cap_id),
+ mWidth(aWidth), mHeight(aHeight) {}
+
+ NS_IMETHOD Run() override {
+ if (mParent->IsShuttingDown()) {
+ // Communication channel is being torn down
+ LOG(("FrameSizeChangeRunnable is active without active Child"));
+ mResult = 0;
+ return NS_OK;
+ }
+ if (!mParent->SendFrameSizeChange(mCapEngine, mCapId, mWidth, mHeight)) {
+ mResult = -1;
+ } else {
+ mResult = 0;
+ }
+ return NS_OK;
+ }
+
+ int GetResult() {
+ return mResult;
+ }
+
+private:
+ RefPtr<CamerasParent> mParent;
+ CaptureEngine mCapEngine;
+ int mCapId;
+ unsigned int mWidth;
+ unsigned int mHeight;
+ int mResult;
+};
+
+int
+CallbackHelper::FrameSizeChange(unsigned int w, unsigned int h,
+ unsigned int streams)
+{
+ LOG(("CallbackHelper Video FrameSizeChange: %ux%u", w, h));
+ RefPtr<FrameSizeChangeRunnable> runnable =
+ new FrameSizeChangeRunnable(mParent, mCapEngine, mCapturerId, w, h);
+ MOZ_ASSERT(mParent);
+ nsIThread * thread = mParent->GetBackgroundThread();
+ MOZ_ASSERT(thread != nullptr);
+ thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ return 0;
+}
+
+class DeliverFrameRunnable : public Runnable {
+public:
+ DeliverFrameRunnable(CamerasParent *aParent,
+ CaptureEngine engine,
+ int cap_id,
+ ShmemBuffer buffer,
+ unsigned char* altbuffer,
+ size_t size,
+ uint32_t time_stamp,
+ int64_t ntp_time,
+ int64_t render_time)
+ : mParent(aParent), mCapEngine(engine), mCapId(cap_id), mBuffer(Move(buffer)),
+ mSize(size), mTimeStamp(time_stamp), mNtpTime(ntp_time),
+ mRenderTime(render_time) {
+ // No ShmemBuffer (of the right size) was available, so make an
+ // extra buffer here. We have no idea when we are going to run and
+ // it will be potentially long after the webrtc frame callback has
+ // returned, so the copy needs to be no later than here.
+ // We will need to copy this back into a Shmem later on so we prefer
+ // using ShmemBuffers to avoid the extra copy.
+ if (altbuffer != nullptr) {
+ mAlternateBuffer.reset(new unsigned char[size]);
+ memcpy(mAlternateBuffer.get(), altbuffer, size);
+ }
+ };
+
+ NS_IMETHOD Run() override {
+ if (mParent->IsShuttingDown()) {
+ // Communication channel is being torn down
+ mResult = 0;
+ return NS_OK;
+ }
+ if (!mParent->DeliverFrameOverIPC(mCapEngine, mCapId,
+ Move(mBuffer), mAlternateBuffer.get(),
+ mSize, mTimeStamp,
+ mNtpTime, mRenderTime)) {
+ mResult = -1;
+ } else {
+ mResult = 0;
+ }
+ return NS_OK;
+ }
+
+ int GetResult() {
+ return mResult;
+ }
+
+private:
+ RefPtr<CamerasParent> mParent;
+ CaptureEngine mCapEngine;
+ int mCapId;
+ ShmemBuffer mBuffer;
+ mozilla::UniquePtr<unsigned char[]> mAlternateBuffer;
+ size_t mSize;
+ uint32_t mTimeStamp;
+ int64_t mNtpTime;
+ int64_t mRenderTime;
+ int mResult;
+};
+
+NS_IMPL_ISUPPORTS(CamerasParent, nsIObserver)
+
+NS_IMETHODIMP
+CamerasParent::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID));
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ MOZ_ASSERT(obs);
+ obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
+ StopVideoCapture();
+ return NS_OK;
+}
+
+nsresult
+CamerasParent::DispatchToVideoCaptureThread(Runnable* event)
+{
+ // Don't try to dispatch if we're already on the right thread.
+ // There's a potential deadlock because the mThreadMonitor is likely
+ // to be taken already.
+ MOZ_ASSERT(!mVideoCaptureThread ||
+ mVideoCaptureThread->thread_id() != PlatformThread::CurrentId());
+
+ MonitorAutoLock lock(mThreadMonitor);
+
+ while(mChildIsAlive && mWebRTCAlive &&
+ (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning())) {
+ mThreadMonitor.Wait();
+ }
+ if (!mVideoCaptureThread || !mVideoCaptureThread->IsRunning()) {
+ return NS_ERROR_FAILURE;
+ }
+ RefPtr<Runnable> addrefedEvent = event;
+ mVideoCaptureThread->message_loop()->PostTask(addrefedEvent.forget());
+ return NS_OK;
+}
+
+void
+CamerasParent::StopVideoCapture()
+{
+ LOG((__PRETTY_FUNCTION__));
+ // We are called from the main thread (xpcom-shutdown) or
+ // from PBackground (when the Actor shuts down).
+ // Shut down the WebRTC stack (on the capture thread)
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self]() -> nsresult {
+ MonitorAutoLock lock(self->mThreadMonitor);
+ self->CloseEngines();
+ self->mThreadMonitor.NotifyAll();
+ return NS_OK;
+ });
+ DebugOnly<nsresult> rv = DispatchToVideoCaptureThread(webrtc_runnable);
+#ifdef DEBUG
+ // It's ok for the dispatch to fail if the cleanup it has to do
+ // has been done already.
+ MOZ_ASSERT(NS_SUCCEEDED(rv) || !mWebRTCAlive);
+#endif
+ // Hold here until the WebRTC thread is gone. We need to dispatch
+ // the thread deletion *now*, or there will be no more possibility
+ // to get to the main thread.
+ MonitorAutoLock lock(mThreadMonitor);
+ while (mWebRTCAlive) {
+ mThreadMonitor.Wait();
+ }
+ // After closing the WebRTC stack, clean up the
+ // VideoCapture thread.
+ if (self->mVideoCaptureThread) {
+ base::Thread *thread = self->mVideoCaptureThread;
+ self->mVideoCaptureThread = nullptr;
+ RefPtr<Runnable> threadShutdown =
+ media::NewRunnableFrom([thread]() -> nsresult {
+ if (thread->IsRunning()) {
+ thread->Stop();
+ }
+ delete thread;
+ return NS_OK;
+ });
+ if (NS_FAILED(NS_DispatchToMainThread(threadShutdown))) {
+ LOG(("Could not dispatch VideoCaptureThread destruction"));
+ }
+ }
+}
+
+int
+CamerasParent::DeliverFrameOverIPC(CaptureEngine cap_engine,
+ int cap_id,
+ ShmemBuffer buffer,
+ unsigned char* altbuffer,
+ size_t size,
+ uint32_t time_stamp,
+ int64_t ntp_time,
+ int64_t render_time)
+{
+ // No ShmemBuffers were available, so construct one now of the right size
+ // and copy into it. That is an extra copy, but we expect this to be
+ // the exceptional case, because we just assured the next call *will* have a
+ // buffer of the right size.
+ if (altbuffer != nullptr) {
+ // Get a shared memory buffer from the pool, at least size big
+ ShmemBuffer shMemBuff = mShmemPool.Get(this, size);
+
+ if (!shMemBuff.Valid()) {
+ LOG(("No usable Video shmem in DeliverFrame (out of buffers?)"));
+ // We can skip this frame if we run out of buffers, it's not a real error.
+ return 0;
+ }
+
+ // get() and Size() check for proper alignment of the segment
+ memcpy(shMemBuff.GetBytes(), altbuffer, size);
+
+ if (!SendDeliverFrame(cap_engine, cap_id,
+ shMemBuff.Get(), size,
+ time_stamp, ntp_time, render_time)) {
+ return -1;
+ }
+ } else {
+ MOZ_ASSERT(buffer.Valid());
+ // ShmemBuffer was available, we're all good. A single copy happened
+ // in the original webrtc callback.
+ if (!SendDeliverFrame(cap_engine, cap_id,
+ buffer.Get(), size,
+ time_stamp, ntp_time, render_time)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+ShmemBuffer
+CamerasParent::GetBuffer(size_t aSize)
+{
+ return mShmemPool.GetIfAvailable(aSize);
+}
+
+int
+CallbackHelper::DeliverFrame(unsigned char* buffer,
+ size_t size,
+ uint32_t time_stamp,
+ int64_t ntp_time,
+ int64_t render_time,
+ void *handle)
+{
+ // Get a shared memory buffer to copy the frame data into
+ ShmemBuffer shMemBuffer = mParent->GetBuffer(size);
+ if (!shMemBuffer.Valid()) {
+ // Either we ran out of buffers or they're not the right size yet
+ LOG(("Correctly sized Video shmem not available in DeliverFrame"));
+ // We will do the copy into a(n extra) temporary buffer inside
+ // the DeliverFrameRunnable constructor.
+ } else {
+ // Shared memory buffers of the right size are available, do the copy here.
+ memcpy(shMemBuffer.GetBytes(), buffer, size);
+ // Mark the original buffer as cleared.
+ buffer = nullptr;
+ }
+ RefPtr<DeliverFrameRunnable> runnable =
+ new DeliverFrameRunnable(mParent, mCapEngine, mCapturerId,
+ Move(shMemBuffer), buffer, size, time_stamp,
+ ntp_time, render_time);
+ MOZ_ASSERT(mParent);
+ nsIThread* thread = mParent->GetBackgroundThread();
+ MOZ_ASSERT(thread != nullptr);
+ thread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+ return 0;
+}
+// XXX!!! FIX THIS -- we should move to pure DeliverI420Frame
+int
+CallbackHelper::DeliverI420Frame(const webrtc::I420VideoFrame& webrtc_frame)
+{
+ return DeliverFrame(const_cast<uint8_t*>(webrtc_frame.buffer(webrtc::kYPlane)),
+ CalcBufferSize(webrtc::kI420, webrtc_frame.width(), webrtc_frame.height()),
+ webrtc_frame.timestamp(),
+ webrtc_frame.ntp_time_ms(),
+ webrtc_frame.render_time_ms(),
+ (void*) webrtc_frame.native_handle());
+}
+
+bool
+CamerasParent::RecvReleaseFrame(mozilla::ipc::Shmem&& s) {
+ mShmemPool.Put(ShmemBuffer(s));
+ return true;
+}
+
+bool
+CamerasParent::SetupEngine(CaptureEngine aCapEngine)
+{
+ MOZ_ASSERT(mVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
+ EngineHelper *helper = &mEngines[aCapEngine];
+
+ // Already initialized
+ if (helper->mEngine) {
+ return true;
+ }
+
+ webrtc::CaptureDeviceInfo *captureDeviceInfo = nullptr;
+
+ switch (aCapEngine) {
+ case ScreenEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Screen);
+ break;
+ case BrowserEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Browser);
+ break;
+ case WinEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Window);
+ break;
+ case AppEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Application);
+ break;
+ case CameraEngine:
+ captureDeviceInfo =
+ new webrtc::CaptureDeviceInfo(webrtc::CaptureDeviceType::Camera);
+ break;
+ default:
+ LOG(("Invalid webrtc Video engine"));
+ MOZ_CRASH();
+ break;
+ }
+
+ helper->mConfig.Set<webrtc::CaptureDeviceInfo>(captureDeviceInfo);
+ helper->mEngine = webrtc::VideoEngine::Create(helper->mConfig);
+
+ if (!helper->mEngine) {
+ LOG(("VideoEngine::Create failed"));
+ return false;
+ }
+
+ helper->mPtrViEBase = webrtc::ViEBase::GetInterface(helper->mEngine);
+ if (!helper->mPtrViEBase) {
+ LOG(("ViEBase::GetInterface failed"));
+ return false;
+ }
+
+ if (helper->mPtrViEBase->Init() < 0) {
+ LOG(("ViEBase::Init failed"));
+ return false;
+ }
+
+ helper->mPtrViECapture = webrtc::ViECapture::GetInterface(helper->mEngine);
+ if (!helper->mPtrViECapture) {
+ LOG(("ViECapture::GetInterface failed"));
+ return false;
+ }
+
+ RefPtr<InputObserver>* observer = mObservers.AppendElement(new InputObserver(this));
+
+#ifdef DEBUG
+ MOZ_ASSERT(0 == helper->mPtrViECapture->RegisterInputObserver(observer->get()));
+#else
+ helper->mPtrViECapture->RegisterInputObserver(observer->get());
+#endif
+
+ helper->mPtrViERender = webrtc::ViERender::GetInterface(helper->mEngine);
+ if (!helper->mPtrViERender) {
+ LOG(("ViERender::GetInterface failed"));
+ return false;
+ }
+
+ return true;
+}
+
+void
+CamerasParent::CloseEngines()
+{
+ LOG((__PRETTY_FUNCTION__));
+ if (!mWebRTCAlive) {
+ return;
+ }
+ MOZ_ASSERT(mVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
+
+ // Stop the callers
+ while (mCallbacks.Length()) {
+ auto capEngine = mCallbacks[0]->mCapEngine;
+ auto capNum = mCallbacks[0]->mCapturerId;
+ LOG(("Forcing shutdown of engine %d, capturer %d", capEngine, capNum));
+ StopCapture(capEngine, capNum);
+ Unused << ReleaseCaptureDevice(capEngine, capNum);
+ }
+
+ for (int i = 0; i < CaptureEngine::MaxEngine; i++) {
+ if (mEngines[i].mEngineIsRunning) {
+ LOG(("Being closed down while engine %d is running!", i));
+ }
+ if (mEngines[i].mPtrViERender) {
+ mEngines[i].mPtrViERender->Release();
+ mEngines[i].mPtrViERender = nullptr;
+ }
+ if (mEngines[i].mPtrViECapture) {
+#ifdef DEBUG
+ MOZ_ASSERT(0 == mEngines[i].mPtrViECapture->DeregisterInputObserver());
+#else
+ mEngines[i].mPtrViECapture->DeregisterInputObserver();
+#endif
+
+ mEngines[i].mPtrViECapture->Release();
+ mEngines[i].mPtrViECapture = nullptr;
+ }
+ if(mEngines[i].mPtrViEBase) {
+ mEngines[i].mPtrViEBase->Release();
+ mEngines[i].mPtrViEBase = nullptr;
+ }
+ if (mEngines[i].mEngine) {
+ mEngines[i].mEngine->SetTraceCallback(nullptr);
+ webrtc::VideoEngine::Delete(mEngines[i].mEngine);
+ mEngines[i].mEngine = nullptr;
+ }
+ }
+
+ mObservers.Clear();
+
+ mWebRTCAlive = false;
+}
+
+bool
+CamerasParent::EnsureInitialized(int aEngine)
+{
+ LOG((__PRETTY_FUNCTION__));
+ // We're shutting down, don't try to do new WebRTC ops.
+ if (!mWebRTCAlive) {
+ return false;
+ }
+ CaptureEngine capEngine = static_cast<CaptureEngine>(aEngine);
+ if (!SetupEngine(capEngine)) {
+ LOG(("CamerasParent failed to initialize engine"));
+ return false;
+ }
+
+ return true;
+}
+
+// Dispatch the runnable to do the camera operation on the
+// specific Cameras thread, preventing us from blocking, and
+// chain a runnable to send back the result on the IPC thread.
+// It would be nice to get rid of the code duplication here,
+// perhaps via Promises.
+bool
+CamerasParent::RecvNumberOfCaptureDevices(const CaptureEngine& aCapEngine)
+{
+ LOG((__PRETTY_FUNCTION__));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, aCapEngine]() -> nsresult {
+ int num = -1;
+ if (self->EnsureInitialized(aCapEngine)) {
+ num = self->mEngines[aCapEngine].mPtrViECapture->NumberOfCaptureDevices();
+ }
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, num]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ if (num < 0) {
+ LOG(("RecvNumberOfCaptureDevices couldn't find devices"));
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ } else {
+ LOG(("RecvNumberOfCaptureDevices: %d", num));
+ Unused << self->SendReplyNumberOfCaptureDevices(num);
+ return NS_OK;
+ }
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+bool
+CamerasParent::RecvEnsureInitialized(const CaptureEngine& aCapEngine)
+{
+ LOG((__PRETTY_FUNCTION__));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, aCapEngine]() -> nsresult {
+ bool result = self->EnsureInitialized(aCapEngine);
+
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, result]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!result) {
+ LOG(("RecvEnsureInitialized failed"));
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ } else {
+ LOG(("RecvEnsureInitialized succeeded"));
+ Unused << self->SendReplySuccess();
+ return NS_OK;
+ }
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+bool
+CamerasParent::RecvNumberOfCapabilities(const CaptureEngine& aCapEngine,
+ const nsCString& unique_id)
+{
+ LOG((__PRETTY_FUNCTION__));
+ LOG(("Getting caps for %s", unique_id.get()));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, unique_id, aCapEngine]() -> nsresult {
+ int num = -1;
+ if (self->EnsureInitialized(aCapEngine)) {
+ num =
+ self->mEngines[aCapEngine].mPtrViECapture->NumberOfCapabilities(
+ unique_id.get(),
+ MediaEngineSource::kMaxUniqueIdLength);
+ }
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, num]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ if (num < 0) {
+ LOG(("RecvNumberOfCapabilities couldn't find capabilities"));
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ } else {
+ LOG(("RecvNumberOfCapabilities: %d", num));
+ }
+ Unused << self->SendReplyNumberOfCapabilities(num);
+ return NS_OK;
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+bool
+CamerasParent::RecvGetCaptureCapability(const CaptureEngine& aCapEngine,
+ const nsCString& unique_id,
+ const int& num)
+{
+ LOG((__PRETTY_FUNCTION__));
+ LOG(("RecvGetCaptureCapability: %s %d", unique_id.get(), num));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, unique_id, aCapEngine, num]() -> nsresult {
+ webrtc::CaptureCapability webrtcCaps;
+ int error = -1;
+ if (self->EnsureInitialized(aCapEngine)) {
+ error = self->mEngines[aCapEngine].mPtrViECapture->GetCaptureCapability(
+ unique_id.get(), MediaEngineSource::kMaxUniqueIdLength, num, webrtcCaps);
+ }
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, webrtcCaps, error]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ CaptureCapability capCap(webrtcCaps.width,
+ webrtcCaps.height,
+ webrtcCaps.maxFPS,
+ webrtcCaps.expectedCaptureDelay,
+ webrtcCaps.rawType,
+ webrtcCaps.codecType,
+ webrtcCaps.interlaced);
+ LOG(("Capability: %u %u %u %u %d %d",
+ webrtcCaps.width,
+ webrtcCaps.height,
+ webrtcCaps.maxFPS,
+ webrtcCaps.expectedCaptureDelay,
+ webrtcCaps.rawType,
+ webrtcCaps.codecType));
+ if (error) {
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ }
+ Unused << self->SendReplyGetCaptureCapability(capCap);
+ return NS_OK;
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+bool
+CamerasParent::RecvGetCaptureDevice(const CaptureEngine& aCapEngine,
+ const int& aListNumber)
+{
+ LOG((__PRETTY_FUNCTION__));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, aCapEngine, aListNumber]() -> nsresult {
+ char deviceName[MediaEngineSource::kMaxDeviceNameLength];
+ char deviceUniqueId[MediaEngineSource::kMaxUniqueIdLength];
+ nsCString name;
+ nsCString uniqueId;
+ int devicePid = 0;
+ int error = -1;
+ if (self->EnsureInitialized(aCapEngine)) {
+ error = self->mEngines[aCapEngine].mPtrViECapture->GetCaptureDevice(aListNumber,
+ deviceName,
+ sizeof(deviceName),
+ deviceUniqueId,
+ sizeof(deviceUniqueId),
+ &devicePid);
+ }
+ if (!error) {
+ name.Assign(deviceName);
+ uniqueId.Assign(deviceUniqueId);
+ }
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, error, name, uniqueId, devicePid]() {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ if (error) {
+ LOG(("GetCaptureDevice failed: %d", error));
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ }
+ bool scary = (devicePid == getpid());
+
+ LOG(("Returning %s name %s id (pid = %d)%s", name.get(),
+ uniqueId.get(), devicePid, (scary? " (scary)" : "")));
+ Unused << self->SendReplyGetCaptureDevice(name, uniqueId, scary);
+ return NS_OK;
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+static nsresult
+GetPrincipalFromOrigin(const nsACString& aOrigin, nsIPrincipal** aPrincipal)
+{
+ nsAutoCString originNoSuffix;
+ mozilla::PrincipalOriginAttributes attrs;
+ if (!attrs.PopulateFromOrigin(aOrigin, originNoSuffix)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+ principal.forget(aPrincipal);
+ return NS_OK;
+}
+
+// Find out whether the given origin has permission to use the
+// camera. If the permission is not persistent, we'll make it
+// a one-shot by removing the (session) permission.
+static bool
+HasCameraPermission(const nsCString& aOrigin)
+{
+ // Name used with nsIPermissionManager
+ static const char* cameraPermission = "MediaManagerVideo";
+ bool allowed = false;
+ nsresult rv;
+ nsCOMPtr<nsIPermissionManager> mgr =
+ do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIIOService> ioServ(do_GetIOService());
+ nsCOMPtr<nsIURI> uri;
+ rv = ioServ->NewURI(aOrigin, nullptr, nullptr, getter_AddRefs(uri));
+ if (NS_SUCCEEDED(rv)) {
+ // Permanent permissions are only retrievable via principal, not uri
+ nsCOMPtr<nsIPrincipal> principal;
+ rv = GetPrincipalFromOrigin(aOrigin, getter_AddRefs(principal));
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
+ rv = mgr->TestExactPermissionFromPrincipal(principal,
+ cameraPermission,
+ &video);
+ if (NS_SUCCEEDED(rv)) {
+ allowed = (video == nsIPermissionManager::ALLOW_ACTION);
+ }
+ // Session permissions are removed after one use.
+ if (allowed) {
+ mgr->RemoveFromPrincipal(principal, cameraPermission);
+ }
+ }
+ }
+ }
+ return allowed;
+}
+
+bool
+CamerasParent::RecvAllocateCaptureDevice(const CaptureEngine& aCapEngine,
+ const nsCString& unique_id,
+ const nsCString& aOrigin)
+{
+ LOG(("%s: Verifying permissions for %s", __PRETTY_FUNCTION__, aOrigin.get()));
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> mainthread_runnable =
+ media::NewRunnableFrom([self, aCapEngine, unique_id, aOrigin]() -> nsresult {
+ // Verify whether the claimed origin has received permission
+ // to use the camera, either persistently or this session (one shot).
+ bool allowed = HasCameraPermission(aOrigin);
+ if (!allowed) {
+ // Developer preference for turning off permission check.
+ if (Preferences::GetBool("media.navigator.permission.disabled", false)
+ || Preferences::GetBool("media.navigator.permission.fake")) {
+ allowed = true;
+ LOG(("No permission but checks are disabled or fake sources active"));
+ } else {
+ LOG(("No camera permission for this origin"));
+ }
+ }
+ // After retrieving the permission (or not) on the main thread,
+ // bounce to the WebRTC thread to allocate the device (or not),
+ // then bounce back to the IPC thread for the reply to content.
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, allowed, aCapEngine, unique_id]() -> nsresult {
+ int numdev = -1;
+ int error = -1;
+ if (allowed && self->EnsureInitialized(aCapEngine)) {
+ error = self->mEngines[aCapEngine].mPtrViECapture->AllocateCaptureDevice(
+ unique_id.get(), MediaEngineSource::kMaxUniqueIdLength, numdev);
+ }
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, numdev, error]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ if (error) {
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ } else {
+ LOG(("Allocated device nr %d", numdev));
+ Unused << self->SendReplyAllocateCaptureDevice(numdev);
+ return NS_OK;
+ }
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ self->DispatchToVideoCaptureThread(webrtc_runnable);
+ return NS_OK;
+ });
+ NS_DispatchToMainThread(mainthread_runnable);
+ return true;
+}
+
+int
+CamerasParent::ReleaseCaptureDevice(const CaptureEngine& aCapEngine,
+ const int& capnum)
+{
+ int error = -1;
+ if (EnsureInitialized(aCapEngine)) {
+ error = mEngines[aCapEngine].mPtrViECapture->ReleaseCaptureDevice(capnum);
+ }
+ return error;
+}
+
+bool
+CamerasParent::RecvReleaseCaptureDevice(const CaptureEngine& aCapEngine,
+ const int& numdev)
+{
+ LOG((__PRETTY_FUNCTION__));
+ LOG(("RecvReleaseCamera device nr %d", numdev));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, aCapEngine, numdev]() -> nsresult {
+ int error = self->ReleaseCaptureDevice(aCapEngine, numdev);
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, error, numdev]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ LOG(("In Shutdown, not Releasing"));
+ return NS_ERROR_FAILURE;
+ }
+ if (error) {
+ Unused << self->SendReplyFailure();
+ LOG(("Failed to free device nr %d", numdev));
+ return NS_ERROR_FAILURE;
+ } else {
+ Unused << self->SendReplySuccess();
+ LOG(("Freed device nr %d", numdev));
+ return NS_OK;
+ }
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+bool
+CamerasParent::RecvStartCapture(const CaptureEngine& aCapEngine,
+ const int& capnum,
+ const CaptureCapability& ipcCaps)
+{
+ LOG((__PRETTY_FUNCTION__));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, aCapEngine, capnum, ipcCaps]() -> nsresult {
+ CallbackHelper** cbh;
+ webrtc::ExternalRenderer* render;
+ EngineHelper* helper = nullptr;
+ int error = -1;
+ if (self->EnsureInitialized(aCapEngine)) {
+ cbh = self->mCallbacks.AppendElement(
+ new CallbackHelper(static_cast<CaptureEngine>(aCapEngine), capnum, self));
+ render = static_cast<webrtc::ExternalRenderer*>(*cbh);
+
+ helper = &self->mEngines[aCapEngine];
+ error =
+ helper->mPtrViERender->AddRenderer(capnum, webrtc::kVideoI420, render);
+ if (!error) {
+ error = helper->mPtrViERender->StartRender(capnum);
+ }
+
+ webrtc::CaptureCapability capability;
+ capability.width = ipcCaps.width();
+ capability.height = ipcCaps.height();
+ capability.maxFPS = ipcCaps.maxFPS();
+ capability.expectedCaptureDelay = ipcCaps.expectedCaptureDelay();
+ capability.rawType = static_cast<webrtc::RawVideoType>(ipcCaps.rawType());
+ capability.codecType = static_cast<webrtc::VideoCodecType>(ipcCaps.codecType());
+ capability.interlaced = ipcCaps.interlaced();
+
+ if (!error) {
+ error = helper->mPtrViECapture->StartCapture(capnum, capability);
+ }
+ if (!error) {
+ helper->mEngineIsRunning = true;
+ }
+ }
+ RefPtr<nsIRunnable> ipc_runnable =
+ media::NewRunnableFrom([self, error]() -> nsresult {
+ if (self->IsShuttingDown()) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!error) {
+ Unused << self->SendReplySuccess();
+ return NS_OK;
+ } else {
+ Unused << self->SendReplyFailure();
+ return NS_ERROR_FAILURE;
+ }
+ });
+ self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
+ return NS_OK;
+ });
+ DispatchToVideoCaptureThread(webrtc_runnable);
+ return true;
+}
+
+void
+CamerasParent::StopCapture(const CaptureEngine& aCapEngine,
+ const int& capnum)
+{
+ if (EnsureInitialized(aCapEngine)) {
+ mEngines[aCapEngine].mPtrViECapture->StopCapture(capnum);
+ mEngines[aCapEngine].mPtrViERender->StopRender(capnum);
+ mEngines[aCapEngine].mPtrViERender->RemoveRenderer(capnum);
+ mEngines[aCapEngine].mEngineIsRunning = false;
+
+ for (size_t i = 0; i < mCallbacks.Length(); i++) {
+ if (mCallbacks[i]->mCapEngine == aCapEngine
+ && mCallbacks[i]->mCapturerId == capnum) {
+ delete mCallbacks[i];
+ mCallbacks.RemoveElementAt(i);
+ break;
+ }
+ }
+ }
+}
+
+bool
+CamerasParent::RecvStopCapture(const CaptureEngine& aCapEngine,
+ const int& capnum)
+{
+ LOG((__PRETTY_FUNCTION__));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> webrtc_runnable =
+ media::NewRunnableFrom([self, aCapEngine, capnum]() -> nsresult {
+ self->StopCapture(aCapEngine, capnum);
+ return NS_OK;
+ });
+ nsresult rv = DispatchToVideoCaptureThread(webrtc_runnable);
+ if (self->IsShuttingDown()) {
+ return NS_SUCCEEDED(rv);
+ } else {
+ if (NS_SUCCEEDED(rv)) {
+ return SendReplySuccess();
+ } else {
+ return SendReplyFailure();
+ }
+ }
+}
+
+void
+CamerasParent::StopIPC()
+{
+ MOZ_ASSERT(!mDestroyed);
+ // Release shared memory now, it's our last chance
+ mShmemPool.Cleanup(this);
+ // We don't want to receive callbacks or anything if we can't
+ // forward them anymore anyway.
+ mChildIsAlive = false;
+ mDestroyed = true;
+}
+
+bool
+CamerasParent::RecvAllDone()
+{
+ LOG((__PRETTY_FUNCTION__));
+ // Don't try to send anything to the child now
+ mChildIsAlive = false;
+ return Send__delete__(this);
+}
+
+void
+CamerasParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // No more IPC from here
+ LOG((__PRETTY_FUNCTION__));
+ StopIPC();
+ // Shut down WebRTC (if we're not in full shutdown, else this
+ // will already have happened)
+ StopVideoCapture();
+}
+
+CamerasParent::CamerasParent()
+ : mShmemPool(CaptureEngine::MaxEngine),
+ mThreadMonitor("CamerasParent::mThreadMonitor"),
+ mVideoCaptureThread(nullptr),
+ mChildIsAlive(true),
+ mDestroyed(false),
+ mWebRTCAlive(true)
+{
+ LOG(("CamerasParent: %p", this));
+
+ mPBackgroundThread = NS_GetCurrentThread();
+ MOZ_ASSERT(mPBackgroundThread != nullptr, "GetCurrentThread failed");
+
+ LOG(("Spinning up WebRTC Cameras Thread"));
+
+ RefPtr<CamerasParent> self(this);
+ RefPtr<Runnable> threadStart =
+ media::NewRunnableFrom([self]() -> nsresult {
+ // Register thread shutdown observer
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (NS_WARN_IF(!obs)) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv =
+ obs->AddObserver(self, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // Start the thread
+ MonitorAutoLock lock(self->mThreadMonitor);
+ self->mVideoCaptureThread = new base::Thread("VideoCapture");
+ base::Thread::Options options;
+#if defined(_WIN32)
+ options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
+#else
+
+ options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
+#endif
+ if (!self->mVideoCaptureThread->StartWithOptions(options)) {
+ MOZ_CRASH();
+ }
+ self->mThreadMonitor.NotifyAll();
+ return NS_OK;
+ });
+ NS_DispatchToMainThread(threadStart);
+
+ MOZ_COUNT_CTOR(CamerasParent);
+}
+
+CamerasParent::~CamerasParent()
+{
+ LOG(("~CamerasParent: %p", this));
+
+ MOZ_COUNT_DTOR(CamerasParent);
+#ifdef DEBUG
+ // Verify we have shut down the webrtc engines, this is
+ // supposed to happen in ActorDestroy.
+ // That runnable takes a ref to us, so it must have finished
+ // by the time we get here.
+ for (int i = 0; i < CaptureEngine::MaxEngine; i++) {
+ MOZ_ASSERT(!mEngines[i].mEngine);
+ }
+#endif
+}
+
+already_AddRefed<CamerasParent>
+CamerasParent::Create() {
+ mozilla::ipc::AssertIsOnBackgroundThread();
+ RefPtr<CamerasParent> camerasParent = new CamerasParent();
+ return camerasParent.forget();
+}
+
+}
+}
diff --git a/dom/media/systemservices/CamerasParent.h b/dom/media/systemservices/CamerasParent.h
new file mode 100644
index 000000000..2c1869410
--- /dev/null
+++ b/dom/media/systemservices/CamerasParent.h
@@ -0,0 +1,187 @@
+/* -*- 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_CamerasParent_h
+#define mozilla_CamerasParent_h
+
+#include "nsIObserver.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/camera/PCamerasParent.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/ShmemPool.h"
+#include "mozilla/Atomics.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"
+#include "CamerasChild.h"
+
+#include "base/thread.h"
+
+namespace mozilla {
+namespace camera {
+
+class CamerasParent;
+
+class CallbackHelper : public webrtc::ExternalRenderer
+{
+public:
+ CallbackHelper(CaptureEngine aCapEng, int aCapId, CamerasParent *aParent)
+ : mCapEngine(aCapEng), mCapturerId(aCapId), mParent(aParent) {};
+
+ // ViEExternalRenderer implementation. These callbacks end up
+ // running on the VideoCapture thread.
+ virtual int FrameSizeChange(unsigned int w, unsigned int h,
+ unsigned int streams) override;
+ virtual int DeliverFrame(unsigned char* buffer,
+ size_t size,
+ uint32_t time_stamp,
+ int64_t ntp_time,
+ int64_t render_time,
+ void *handle) override;
+ virtual int DeliverI420Frame(const webrtc::I420VideoFrame& webrtc_frame) override;
+ virtual bool IsTextureSupported() override { return false; };
+
+ friend CamerasParent;
+
+private:
+ CaptureEngine mCapEngine;
+ int mCapturerId;
+ CamerasParent *mParent;
+};
+
+class EngineHelper
+{
+public:
+ EngineHelper() :
+ mEngine(nullptr), mPtrViEBase(nullptr), mPtrViECapture(nullptr),
+ mPtrViERender(nullptr), mEngineIsRunning(false) {};
+
+ webrtc::VideoEngine *mEngine;
+ webrtc::ViEBase *mPtrViEBase;
+ webrtc::ViECapture *mPtrViECapture;
+ webrtc::ViERender *mPtrViERender;
+
+ // The webrtc code keeps a reference to this one.
+ webrtc::Config mConfig;
+
+ // Engine alive
+ bool mEngineIsRunning;
+};
+
+class InputObserver : public webrtc::ViEInputObserver
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(InputObserver)
+
+ explicit InputObserver(CamerasParent* aParent)
+ : mParent(aParent) {};
+ virtual void DeviceChange();
+
+ friend CamerasParent;
+
+private:
+ ~InputObserver() {}
+
+ RefPtr<CamerasParent> mParent;
+};
+
+class CamerasParent : public PCamerasParent,
+ public nsIObserver
+{
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+public:
+ static already_AddRefed<CamerasParent> Create();
+
+ // Messages received form the child. These run on the IPC/PBackground thread.
+ virtual bool RecvAllocateCaptureDevice(const CaptureEngine&, const nsCString&,
+ const nsCString&) override;
+ virtual bool RecvReleaseCaptureDevice(const CaptureEngine&,
+ const int&) override;
+ virtual bool RecvNumberOfCaptureDevices(const CaptureEngine&) override;
+ virtual bool RecvNumberOfCapabilities(const CaptureEngine&,
+ const nsCString&) override;
+ virtual bool RecvGetCaptureCapability(const CaptureEngine&, const nsCString&,
+ const int&) override;
+ virtual bool RecvGetCaptureDevice(const CaptureEngine&, const int&) override;
+ virtual bool RecvStartCapture(const CaptureEngine&, const int&,
+ const CaptureCapability&) override;
+ virtual bool RecvStopCapture(const CaptureEngine&, const int&) override;
+ virtual bool RecvReleaseFrame(mozilla::ipc::Shmem&&) override;
+ virtual bool RecvAllDone() override;
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ virtual bool RecvEnsureInitialized(const CaptureEngine&) override;
+
+ nsIThread* GetBackgroundThread() { return mPBackgroundThread; };
+ bool IsShuttingDown() { return !mChildIsAlive
+ || mDestroyed
+ || !mWebRTCAlive; };
+ ShmemBuffer GetBuffer(size_t aSize);
+
+ // helper to forward to the PBackground thread
+ int DeliverFrameOverIPC(CaptureEngine capEng,
+ int cap_id,
+ ShmemBuffer buffer,
+ unsigned char* altbuffer,
+ size_t size,
+ uint32_t time_stamp,
+ int64_t ntp_time,
+ int64_t render_time);
+
+
+ CamerasParent();
+
+protected:
+ virtual ~CamerasParent();
+
+ // We use these helpers for shutdown and for the respective IPC commands.
+ void StopCapture(const CaptureEngine& aCapEngine, const int& capnum);
+ int ReleaseCaptureDevice(const CaptureEngine& aCapEngine, const int& capnum);
+
+ bool SetupEngine(CaptureEngine aCapEngine);
+ bool EnsureInitialized(int aEngine);
+ void CloseEngines();
+ void StopIPC();
+ void StopVideoCapture();
+ // Can't take already_AddRefed because it can fail in stupid ways.
+ nsresult DispatchToVideoCaptureThread(Runnable* event);
+
+ EngineHelper mEngines[CaptureEngine::MaxEngine];
+ nsTArray<CallbackHelper*> mCallbacks;
+
+ // image buffers
+ mozilla::ShmemPool mShmemPool;
+
+ // PBackground parent thread
+ nsCOMPtr<nsIThread> mPBackgroundThread;
+
+ // Monitors creation of the thread below
+ Monitor mThreadMonitor;
+
+ // video processing thread - where webrtc.org capturer code runs
+ base::Thread* mVideoCaptureThread;
+
+ // Shutdown handling
+ bool mChildIsAlive;
+ bool mDestroyed;
+ // Above 2 are PBackground only, but this is potentially
+ // read cross-thread.
+ mozilla::Atomic<bool> mWebRTCAlive;
+ nsTArray<RefPtr<InputObserver>> mObservers;
+};
+
+PCamerasParent* CreateCamerasParent();
+
+} // namespace camera
+} // namespace mozilla
+
+#endif // mozilla_CameraParent_h
diff --git a/dom/media/systemservices/CamerasTypes.h b/dom/media/systemservices/CamerasTypes.h
new file mode 100644
index 000000000..9ae564e9a
--- /dev/null
+++ b/dom/media/systemservices/CamerasTypes.h
@@ -0,0 +1,38 @@
+/* -*- 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_CamerasTypes_h
+#define mozilla_CamerasTypes_h
+
+#include "ipc/IPCMessageUtils.h"
+
+namespace mozilla {
+
+namespace camera {
+
+enum CaptureEngine : int {
+ InvalidEngine = 0,
+ ScreenEngine,
+ BrowserEngine,
+ WinEngine,
+ AppEngine,
+ CameraEngine,
+ MaxEngine
+};
+
+} // namespace camera
+} // namespace mozilla
+
+namespace IPC {
+template<>
+struct ParamTraits<mozilla::camera::CaptureEngine> :
+ public ContiguousEnumSerializer<mozilla::camera::CaptureEngine,
+ mozilla::camera::CaptureEngine::InvalidEngine,
+ mozilla::camera::CaptureEngine::MaxEngine>
+{ };
+}
+
+#endif // mozilla_CamerasTypes_h
diff --git a/dom/media/systemservices/DeviceChangeCallback.h b/dom/media/systemservices/DeviceChangeCallback.h
new file mode 100644
index 000000000..d153e9cd4
--- /dev/null
+++ b/dom/media/systemservices/DeviceChangeCallback.h
@@ -0,0 +1,61 @@
+/* -*- 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_DeviceChangeCallback_h
+#define mozilla_DeviceChangeCallback_h
+
+namespace mozilla {
+
+class DeviceChangeCallback
+{
+public:
+ virtual void OnDeviceChange()
+ {
+ MutexAutoLock lock(mCallbackMutex);
+ for (DeviceChangeCallback* observer : mDeviceChangeCallbackList)
+ {
+ observer->OnDeviceChange();
+ }
+ }
+
+ virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
+ {
+ MutexAutoLock lock(mCallbackMutex);
+ if (mDeviceChangeCallbackList.IndexOf(aCallback) == mDeviceChangeCallbackList.NoIndex)
+ mDeviceChangeCallbackList.AppendElement(aCallback);
+ return 0;
+ }
+
+ virtual int RemoveDeviceChangeCallback(DeviceChangeCallback* aCallback)
+ {
+ MutexAutoLock lock(mCallbackMutex);
+ return RemoveDeviceChangeCallbackLocked(aCallback);
+ }
+
+ virtual int RemoveDeviceChangeCallbackLocked(DeviceChangeCallback* aCallback)
+ {
+ mCallbackMutex.AssertCurrentThreadOwns();
+ if (mDeviceChangeCallbackList.IndexOf(aCallback) != mDeviceChangeCallbackList.NoIndex)
+ mDeviceChangeCallbackList.RemoveElement(aCallback);
+ return 0;
+ }
+
+ DeviceChangeCallback() : mCallbackMutex("mozilla::media::DeviceChangeCallback::mCallbackMutex")
+ {
+ }
+
+ virtual ~DeviceChangeCallback()
+ {
+ }
+
+protected:
+ nsTArray<DeviceChangeCallback*> mDeviceChangeCallbackList;
+ Mutex mCallbackMutex;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_DeviceChangeCallback_h
diff --git a/dom/media/systemservices/LoadManager.cpp b/dom/media/systemservices/LoadManager.cpp
new file mode 100644
index 000000000..f0f4f83a7
--- /dev/null
+++ b/dom/media/systemservices/LoadManager.cpp
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 50; 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/. */
+
+#include "LoadManager.h"
+#include "LoadMonitor.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "prtime.h"
+#include "prinrval.h"
+#include "prsystem.h"
+
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsReadableUtils.h"
+#include "nsIObserverService.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/ArrayUtils.h"
+
+// MOZ_LOG=LoadManager:5
+mozilla::LazyLogModule gLoadManagerLog("LoadManager");
+#undef LOG
+#undef LOG_ENABLED
+#define LOG(args) MOZ_LOG(gLoadManagerLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gLoadManagerLog, mozilla::LogLevel::Verbose)
+
+namespace mozilla {
+
+/* static */ StaticRefPtr<LoadManagerSingleton> LoadManagerSingleton::sSingleton;
+
+NS_IMPL_ISUPPORTS(LoadManagerSingleton, nsIObserver)
+
+
+LoadManagerSingleton::LoadManagerSingleton(bool aEncoderOnly,
+ int aLoadMeasurementInterval,
+ int aAveragingMeasurements,
+ float aHighLoadThreshold,
+ float aLowLoadThreshold)
+ : mLock("LoadManager"),
+ mCurrentState(webrtc::kLoadNormal),
+ mOveruseActive(false),
+ mLoadSum(0.0f),
+ mLoadSumMeasurements(0),
+ mLoadMeasurementInterval(aLoadMeasurementInterval),
+ mAveragingMeasurements(aAveragingMeasurements),
+ mHighLoadThreshold(aHighLoadThreshold),
+ mLowLoadThreshold(aLowLoadThreshold)
+{
+ LOG(("LoadManager - Initializing (%dms x %d, %f, %f)",
+ mLoadMeasurementInterval, mAveragingMeasurements,
+ mHighLoadThreshold, mLowLoadThreshold));
+ MOZ_ASSERT(mHighLoadThreshold > mLowLoadThreshold);
+ if (!aEncoderOnly) {
+ mLoadMonitor = new LoadMonitor(mLoadMeasurementInterval);
+ mLoadMonitor->Init(mLoadMonitor);
+ mLoadMonitor->SetLoadChangeCallback(this);
+ }
+
+ mLastStateChange = TimeStamp::Now();
+ for (auto &in_state : mTimeInState) {
+ in_state = 0;
+ }
+}
+
+LoadManagerSingleton::~LoadManagerSingleton()
+{
+ LOG(("LoadManager: shutting down LoadMonitor"));
+ MOZ_ASSERT(!mLoadMonitor, "why wasn't the LoadMonitor shut down in xpcom-shutdown?");
+ if (mLoadMonitor) {
+ mLoadMonitor->Shutdown();
+ }
+}
+
+nsresult
+LoadManagerSingleton::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Observer invoked off the main thread");
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+
+ if (!strcmp(aTopic, "xpcom-shutdown")) {
+ obs->RemoveObserver(this, "xpcom-shutdown");
+ {
+ MutexAutoLock lock(mLock);
+ mObservers.Clear();
+ }
+ if (mLoadMonitor) {
+ mLoadMonitor->Shutdown();
+ mLoadMonitor = nullptr;
+ }
+
+ LOG(("Releasing LoadManager singleton and thread"));
+ // Note: won't be released immediately as the Observer has a ref to us
+ sSingleton = nullptr;
+ }
+ return NS_OK;
+}
+
+void
+LoadManagerSingleton::LoadChanged(float aSystemLoad, float aProcesLoad)
+{
+ MutexAutoLock lock(mLock);
+ // Update total load, and total amount of measured seconds.
+ mLoadSum += aSystemLoad;
+ mLoadSumMeasurements++;
+
+ if (mLoadSumMeasurements >= mAveragingMeasurements) {
+ double averagedLoad = mLoadSum / (float)mLoadSumMeasurements;
+
+ webrtc::CPULoadState newState = mCurrentState;
+
+ if (mOveruseActive || averagedLoad > mHighLoadThreshold) {
+ LOG(("LoadManager - LoadStressed"));
+ newState = webrtc::kLoadStressed;
+ } else if (averagedLoad < mLowLoadThreshold) {
+ LOG(("LoadManager - LoadRelaxed"));
+ newState = webrtc::kLoadRelaxed;
+ } else {
+ LOG(("LoadManager - LoadNormal"));
+ newState = webrtc::kLoadNormal;
+ }
+
+ if (newState != mCurrentState) {
+ LoadHasChanged(newState);
+ }
+
+ mLoadSum = 0;
+ mLoadSumMeasurements = 0;
+ }
+}
+
+void
+LoadManagerSingleton::OveruseDetected()
+{
+ LOG(("LoadManager - Overuse Detected"));
+ MutexAutoLock lock(mLock);
+ mOveruseActive = true;
+ if (mCurrentState != webrtc::kLoadStressed) {
+ LoadHasChanged(webrtc::kLoadStressed);
+ }
+}
+
+void
+LoadManagerSingleton::NormalUsage()
+{
+ LOG(("LoadManager - Overuse finished"));
+ MutexAutoLock lock(mLock);
+ mOveruseActive = false;
+}
+
+void
+LoadManagerSingleton::LoadHasChanged(webrtc::CPULoadState aNewState)
+{
+ mLock.AssertCurrentThreadOwns();
+ LOG(("LoadManager - Signaling LoadHasChanged from %d to %d to %d listeners",
+ mCurrentState, aNewState, mObservers.Length()));
+
+ // Record how long we spent in this state for later Telemetry or display
+ TimeStamp now = TimeStamp::Now();
+ mTimeInState[mCurrentState] += (now - mLastStateChange).ToMilliseconds();
+ mLastStateChange = now;
+
+ mCurrentState = aNewState;
+ for (size_t i = 0; i < mObservers.Length(); i++) {
+ mObservers.ElementAt(i)->onLoadStateChanged(mCurrentState);
+ }
+}
+
+void
+LoadManagerSingleton::AddObserver(webrtc::CPULoadStateObserver * aObserver)
+{
+ LOG(("LoadManager - Adding Observer"));
+ MutexAutoLock lock(mLock);
+ mObservers.AppendElement(aObserver);
+}
+
+void
+LoadManagerSingleton::RemoveObserver(webrtc::CPULoadStateObserver * aObserver)
+{
+ LOG(("LoadManager - Removing Observer"));
+ MutexAutoLock lock(mLock);
+ if (!mObservers.RemoveElement(aObserver)) {
+ LOG(("LoadManager - Element to remove not found"));
+ }
+ if (mObservers.Length() == 0) {
+ // Record how long we spent in the final state for later Telemetry or display
+ TimeStamp now = TimeStamp::Now();
+ mTimeInState[mCurrentState] += (now - mLastStateChange).ToMilliseconds();
+
+ float total = 0;
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mTimeInState); i++) {
+ total += mTimeInState[i];
+ }
+ // Don't include short calls; we don't have reasonable load data, and
+ // such short calls rarely reach a stable state. Keep relatively
+ // short calls separate from longer ones
+ bool log = total > 5*PR_MSEC_PER_SEC;
+ bool small = log && total < 30*PR_MSEC_PER_SEC;
+ if (log) {
+ // Note: We don't care about rounding here; thus total may be < 100
+ Telemetry::Accumulate(small ? Telemetry::WEBRTC_LOAD_STATE_RELAXED_SHORT :
+ Telemetry::WEBRTC_LOAD_STATE_RELAXED,
+ (uint32_t) (mTimeInState[webrtc::CPULoadState::kLoadRelaxed]/total * 100));
+ Telemetry::Accumulate(small ? Telemetry::WEBRTC_LOAD_STATE_NORMAL_SHORT :
+ Telemetry::WEBRTC_LOAD_STATE_NORMAL,
+ (uint32_t) (mTimeInState[webrtc::CPULoadState::kLoadNormal]/total * 100));
+ Telemetry::Accumulate(small ? Telemetry::WEBRTC_LOAD_STATE_STRESSED_SHORT :
+ Telemetry::WEBRTC_LOAD_STATE_STRESSED,
+ (uint32_t) (mTimeInState[webrtc::CPULoadState::kLoadStressed]/total * 100));
+ }
+ for (auto &in_state : mTimeInState) {
+ in_state = 0;
+ }
+
+ if (mLoadMonitor) {
+ // Dance to avoid deadlock on mLock!
+ RefPtr<LoadMonitor> loadMonitor = mLoadMonitor.forget();
+ MutexAutoUnlock unlock(mLock);
+
+ loadMonitor->Shutdown();
+ }
+ }
+}
+
+
+}
diff --git a/dom/media/systemservices/LoadManager.h b/dom/media/systemservices/LoadManager.h
new file mode 100644
index 000000000..96824a308
--- /dev/null
+++ b/dom/media/systemservices/LoadManager.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 _LOADMANAGER_H_
+#define _LOADMANAGER_H_
+
+#include "LoadMonitor.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Services.h"
+#include "nsTArray.h"
+#include "nsIObserver.h"
+
+#include "webrtc/common_types.h"
+#include "webrtc/video_engine/include/vie_base.h"
+
+extern mozilla::LazyLogModule gLoadManagerLog;
+
+namespace mozilla {
+
+class LoadManagerSingleton : public LoadNotificationCallback,
+ public webrtc::CPULoadStateCallbackInvoker,
+ public webrtc::CpuOveruseObserver,
+ public nsIObserver
+
+{
+public:
+ static LoadManagerSingleton* Get();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ // LoadNotificationCallback interface
+ void LoadChanged(float aSystemLoad, float aProcessLoad) override;
+ // CpuOveruseObserver interface
+ // Called as soon as an overuse is detected.
+ void OveruseDetected() override;
+ // Called periodically when the system is not overused any longer.
+ void NormalUsage() override;
+ // CPULoadStateCallbackInvoker interface
+ void AddObserver(webrtc::CPULoadStateObserver * aObserver) override;
+ void RemoveObserver(webrtc::CPULoadStateObserver * aObserver) override;
+
+private:
+ LoadManagerSingleton(bool aEncoderOnly,
+ int aLoadMeasurementInterval,
+ int aAveragingMeasurements,
+ float aHighLoadThreshold,
+ float aLowLoadThreshold);
+ ~LoadManagerSingleton();
+
+ void LoadHasChanged(webrtc::CPULoadState aNewState);
+
+ RefPtr<LoadMonitor> mLoadMonitor;
+
+ // This protects access to the mObservers list, the current state, and
+ // pretty much all the other members (below).
+ Mutex mLock;
+ nsTArray<webrtc::CPULoadStateObserver*> mObservers;
+ webrtc::CPULoadState mCurrentState;
+ TimeStamp mLastStateChange;
+ float mTimeInState[static_cast<int>(webrtc::kLoadLast)];
+
+ // Set when overuse was signaled to us, and hasn't been un-signaled yet.
+ bool mOveruseActive;
+ float mLoadSum;
+ int mLoadSumMeasurements;
+ // Load measurement settings
+ int mLoadMeasurementInterval;
+ int mAveragingMeasurements;
+ float mHighLoadThreshold;
+ float mLowLoadThreshold;
+
+ static StaticRefPtr<LoadManagerSingleton> sSingleton;
+};
+
+class LoadManager final : public webrtc::CPULoadStateCallbackInvoker,
+ public webrtc::CpuOveruseObserver
+{
+public:
+ explicit LoadManager(LoadManagerSingleton* aManager)
+ : mManager(aManager)
+ {}
+ ~LoadManager() {}
+
+ void AddObserver(webrtc::CPULoadStateObserver * aObserver) override
+ {
+ mManager->AddObserver(aObserver);
+ }
+ void RemoveObserver(webrtc::CPULoadStateObserver * aObserver) override
+ {
+ mManager->RemoveObserver(aObserver);
+ }
+ void OveruseDetected() override
+ {
+ mManager->OveruseDetected();
+ }
+ void NormalUsage() override
+ {
+ mManager->NormalUsage();
+ }
+
+private:
+ RefPtr<LoadManagerSingleton> mManager;
+};
+
+} //namespace
+
+#endif /* _LOADMANAGER_H_ */
diff --git a/dom/media/systemservices/LoadManagerFactory.cpp b/dom/media/systemservices/LoadManagerFactory.cpp
new file mode 100644
index 000000000..be22a75fe
--- /dev/null
+++ b/dom/media/systemservices/LoadManagerFactory.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 50; 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/. */
+
+#include "LoadManager.h"
+#include "LoadManagerFactory.h"
+#include "MainThreadUtils.h"
+#include "nsIObserverService.h"
+
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+
+// Assume stored in an nsAutoPtr<>
+LoadManager *
+LoadManagerBuild(void)
+{
+ return new LoadManager(LoadManagerSingleton::Get());
+}
+
+/* static */ LoadManagerSingleton*
+LoadManagerSingleton::Get() {
+ if (!sSingleton) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool loadEncoderOnly =
+ mozilla::Preferences::GetBool("media.navigator.load_adapt.encoder_only", true);
+ int loadMeasurementInterval =
+ mozilla::Preferences::GetInt("media.navigator.load_adapt.measure_interval", 1000);
+ int averagingSeconds =
+ mozilla::Preferences::GetInt("media.navigator.load_adapt.avg_seconds", 3);
+ float highLoadThreshold =
+ mozilla::Preferences::GetFloat("media.navigator.load_adapt.high_load", 0.90f);
+ float lowLoadThreshold =
+ mozilla::Preferences::GetFloat("media.navigator.load_adapt.low_load", 0.40f);
+
+ sSingleton = new LoadManagerSingleton(loadEncoderOnly,
+ loadMeasurementInterval,
+ averagingSeconds,
+ highLoadThreshold,
+ lowLoadThreshold);
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(sSingleton, "xpcom-shutdown", false);
+ }
+ }
+ return sSingleton;
+}
+
+}; // namespace
diff --git a/dom/media/systemservices/LoadManagerFactory.h b/dom/media/systemservices/LoadManagerFactory.h
new file mode 100644
index 000000000..4868756f5
--- /dev/null
+++ b/dom/media/systemservices/LoadManagerFactory.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 _LOADMANAGERFACTORY_H_
+#define _LOADMANAGERFACTORY_H_
+
+namespace mozilla {
+
+class LoadManager;
+
+mozilla::LoadManager* LoadManagerBuild();
+void LoadManagerDestroy(mozilla::LoadManager* aLoadManager);
+
+} //namespace
+
+#endif /* _LOADMANAGERFACTORY_H_ */
diff --git a/dom/media/systemservices/LoadMonitor.cpp b/dom/media/systemservices/LoadMonitor.cpp
new file mode 100644
index 000000000..7a64c4fb0
--- /dev/null
+++ b/dom/media/systemservices/LoadMonitor.cpp
@@ -0,0 +1,658 @@
+/* -*- Mode: C++; tab-width: 50; 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/. */
+
+#include "LoadMonitor.h"
+#include "LoadManager.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "prtime.h"
+#include "prinrval.h"
+#include "prsystem.h"
+#include "prprf.h"
+
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsReadableUtils.h"
+#include "nsNetUtil.h"
+#include "nsIInputStream.h"
+#include "nsIFile.h"
+#include "nsILineInputStream.h"
+#include "nsIObserverService.h"
+#include "nsIServiceManager.h"
+
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Services.h"
+
+#ifdef XP_UNIX
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#endif
+
+#ifdef XP_MACOSX
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/host_info.h>
+#endif
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/sysctl.h>
+# if defined(__OpenBSD__)
+#define KERN_CP_TIME KERN_CPTIME
+# endif
+#endif
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/sched.h>
+#endif
+
+#ifdef XP_WIN
+#include <pdh.h>
+#include <tchar.h>
+#pragma comment(lib, "pdh.lib")
+#endif
+
+// MOZ_LOG=LoadManager:5
+#undef LOG
+#undef LOG_ENABLED
+#define LOG(args) MOZ_LOG(gLoadManagerLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gLoadManagerLog, mozilla::LogLevel::Debug)
+#define LOG_MANY_ENABLED() MOZ_LOG_TEST(gLoadManagerLog, mozilla::LogLevel::Verbose)
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(LoadMonitor, nsIObserver)
+
+LoadMonitor::LoadMonitor(int aLoadUpdateInterval)
+ : mLoadUpdateInterval(aLoadUpdateInterval),
+ mLock("LoadMonitor.mLock"),
+ mCondVar(mLock, "LoadMonitor.mCondVar"),
+ mShutdownPending(false),
+ mLoadInfoThread(nullptr),
+ mSystemLoad(0.0f),
+ mProcessLoad(0.0f),
+ mLoadNotificationCallback(nullptr)
+{
+}
+
+LoadMonitor::~LoadMonitor()
+{
+ Shutdown();
+}
+
+NS_IMETHODIMP
+LoadMonitor::Observe(nsISupports* /* aSubject */,
+ const char* aTopic,
+ const char16_t* /* aData */)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+ MOZ_ASSERT(!strcmp("xpcom-shutdown-threads", aTopic), "Bad topic!");
+ Shutdown();
+ return NS_OK;
+}
+
+class LoadMonitorAddObserver : public Runnable
+{
+public:
+ explicit LoadMonitorAddObserver(RefPtr<LoadMonitor> loadMonitor)
+ {
+ mLoadMonitor = loadMonitor;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = observerService->AddObserver(mLoadMonitor, "xpcom-shutdown-threads", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+private:
+ RefPtr<LoadMonitor> mLoadMonitor;
+};
+
+class LoadMonitorRemoveObserver : public Runnable
+{
+public:
+ explicit LoadMonitorRemoveObserver(RefPtr<LoadMonitor> loadMonitor)
+ {
+ mLoadMonitor = loadMonitor;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ // remove xpcom shutdown observer
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService)
+ observerService->RemoveObserver(mLoadMonitor, "xpcom-shutdown-threads");
+
+ return NS_OK;
+ }
+
+private:
+ RefPtr<LoadMonitor> mLoadMonitor;
+};
+
+void LoadMonitor::Shutdown()
+{
+ if (mLoadInfoThread) {
+ {
+ MutexAutoLock lock(mLock);
+ LOG(("LoadMonitor: shutting down"));
+ mShutdownPending = true;
+ mCondVar.Notify();
+ }
+
+ // Note: can't just call ->Shutdown() from here; that spins the event
+ // loop here, causing re-entrancy issues if we're invoked from cycle
+ // collection. Argh.
+ mLoadInfoThread = nullptr;
+
+ RefPtr<LoadMonitorRemoveObserver> remObsRunner = new LoadMonitorRemoveObserver(this);
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(remObsRunner);
+ } else {
+ remObsRunner->Run();
+ }
+ }
+}
+
+#ifdef XP_WIN
+static LPCTSTR TotalCounterPath = _T("\\Processor(_Total)\\% Processor Time");
+
+class WinProcMon
+{
+public:
+ WinProcMon():
+ mQuery(0), mCounter(0) {};
+ ~WinProcMon();
+ nsresult Init();
+ nsresult QuerySystemLoad(float* load_percent);
+ static const uint64_t TicksPerSec = 10000000; //100nsec tick (10MHz)
+private:
+ PDH_HQUERY mQuery;
+ PDH_HCOUNTER mCounter;
+};
+
+WinProcMon::~WinProcMon()
+{
+ if (mQuery != 0) {
+ PdhCloseQuery(mQuery);
+ mQuery = 0;
+ }
+}
+
+nsresult
+WinProcMon::Init()
+{
+ PDH_HQUERY query;
+ PDH_HCOUNTER counter;
+
+ // Get a query handle to the Performance Data Helper
+ PDH_STATUS status = PdhOpenQuery(
+ NULL, // No log file name: use real-time source
+ 0, // zero out user data token: unsued
+ &query);
+
+ if (status != ERROR_SUCCESS) {
+ LOG(("PdhOpenQuery error = %X", status));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Add a pre-defined high performance counter to the query.
+ // This one is for the total CPU usage.
+ status = PdhAddCounter(query, TotalCounterPath, 0, &counter);
+
+ if (status != ERROR_SUCCESS) {
+ PdhCloseQuery(query);
+ LOG(("PdhAddCounter (_Total) error = %X", status));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Need to make an initial query call to set up data capture.
+ status = PdhCollectQueryData(query);
+
+ if (status != ERROR_SUCCESS) {
+ PdhCloseQuery(query);
+ LOG(("PdhCollectQueryData (init) error = %X", status));
+ return NS_ERROR_FAILURE;
+ }
+
+ mQuery = query;
+ mCounter = counter;
+ return NS_OK;
+}
+
+nsresult WinProcMon::QuerySystemLoad(float* load_percent)
+{
+ *load_percent = 0;
+
+ if (mQuery == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Update all counters associated with this query object.
+ PDH_STATUS status = PdhCollectQueryData(mQuery);
+
+ if (status != ERROR_SUCCESS) {
+ LOG(("PdhCollectQueryData error = %X", status));
+ return NS_ERROR_FAILURE;
+ }
+
+ PDH_FMT_COUNTERVALUE counter;
+ // maximum is 100% regardless of CPU core count.
+ status = PdhGetFormattedCounterValue(
+ mCounter,
+ PDH_FMT_DOUBLE,
+ (LPDWORD)NULL,
+ &counter);
+
+ if (ERROR_SUCCESS != status ||
+ // There are multiple success return values.
+ !IsSuccessSeverity(counter.CStatus)) {
+ LOG(("PdhGetFormattedCounterValue error"));
+ return NS_ERROR_FAILURE;
+ }
+
+ // The result is a percent value, reduce to match expected scale.
+ *load_percent = (float)(counter.doubleValue / 100.0f);
+ return NS_OK;
+}
+#endif
+
+// Use a non-generic class name, because otherwise we can get name collisions
+// with other classes in the codebase. The normal way of dealing with that is
+// to put the class in an anonymous namespace, but this class is used as a
+// member of RTCLoadInfo, which can't be in the anonymous namespace, so it also
+// can't be in an anonymous namespace: gcc warns about that setup and this
+// directory is fail-on-warnings.
+class RTCLoadStats
+{
+public:
+ RTCLoadStats() :
+ mPrevTotalTimes(0),
+ mPrevCpuTimes(0),
+ mPrevLoad(0) {};
+
+ double GetLoad() { return (double)mPrevLoad; };
+
+ uint64_t mPrevTotalTimes;
+ uint64_t mPrevCpuTimes;
+ float mPrevLoad; // Previous load value.
+};
+
+// Use a non-generic class name, because otherwise we can get name collisions
+// with other classes in the codebase. The normal way of dealing with that is
+// to put the class in an anonymous namespace, but this class is used as a
+// member of LoadInfoCollectRunner, which can't be in the anonymous namespace,
+// so it also can't be in an anonymous namespace: gcc warns about that setup
+// and this directory is fail-on-warnings.
+class RTCLoadInfo final
+{
+private:
+ ~RTCLoadInfo() {}
+
+public:
+ NS_INLINE_DECL_REFCOUNTING(RTCLoadInfo)
+
+ RTCLoadInfo(): mLoadUpdateInterval(0) {};
+ nsresult Init(int aLoadUpdateInterval);
+ double GetSystemLoad() { return mSystemLoad.GetLoad(); };
+ double GetProcessLoad() { return mProcessLoad.GetLoad(); };
+ nsresult UpdateSystemLoad();
+ nsresult UpdateProcessLoad();
+
+private:
+ void UpdateCpuLoad(uint64_t ticks_per_interval,
+ uint64_t current_total_times,
+ uint64_t current_cpu_times,
+ RTCLoadStats* loadStat);
+#ifdef XP_WIN
+ WinProcMon mSysMon;
+ HANDLE mProcHandle;
+ int mNumProcessors;
+#endif
+ RTCLoadStats mSystemLoad;
+ RTCLoadStats mProcessLoad;
+ uint64_t mTicksPerInterval;
+ int mLoadUpdateInterval;
+};
+
+nsresult RTCLoadInfo::Init(int aLoadUpdateInterval)
+{
+ mLoadUpdateInterval = aLoadUpdateInterval;
+#ifdef XP_WIN
+ mTicksPerInterval = (WinProcMon::TicksPerSec /*Hz*/
+ * mLoadUpdateInterval /*msec*/) / 1000 ;
+ mNumProcessors = PR_GetNumberOfProcessors();
+ mProcHandle = GetCurrentProcess();
+ return mSysMon.Init();
+#else
+ mTicksPerInterval = (sysconf(_SC_CLK_TCK) * mLoadUpdateInterval) / 1000;
+ return NS_OK;
+#endif
+}
+
+void RTCLoadInfo::UpdateCpuLoad(uint64_t ticks_per_interval,
+ uint64_t current_total_times,
+ uint64_t current_cpu_times,
+ RTCLoadStats *loadStat) {
+ // Check if we get an inconsistent number of ticks.
+ if (((current_total_times - loadStat->mPrevTotalTimes)
+ > (ticks_per_interval * 10))
+ || current_total_times < loadStat->mPrevTotalTimes
+ || current_cpu_times < loadStat->mPrevCpuTimes) {
+ // Bug at least on the Nexus 4 and Galaxy S4
+ // https://code.google.com/p/android/issues/detail?id=41630
+ // We do need to update our previous times, or we can get stuck
+ // when there is a blip upwards and then we get a bunch of consecutive
+ // lower times. Just skip the load calculation.
+ LOG(("Inconsistent time values are passed. ignored"));
+ // Try to recover next tick
+ loadStat->mPrevTotalTimes = current_total_times;
+ loadStat->mPrevCpuTimes = current_cpu_times;
+ return;
+ }
+
+ const uint64_t cpu_diff = current_cpu_times - loadStat->mPrevCpuTimes;
+ const uint64_t total_diff = current_total_times - loadStat->mPrevTotalTimes;
+ if (total_diff > 0) {
+#ifdef XP_WIN
+ float result = (float)cpu_diff / (float)total_diff/ (float)mNumProcessors;
+#else
+ float result = (float)cpu_diff / (float)total_diff;
+#endif
+ loadStat->mPrevLoad = result;
+ }
+ loadStat->mPrevTotalTimes = current_total_times;
+ loadStat->mPrevCpuTimes = current_cpu_times;
+}
+
+nsresult RTCLoadInfo::UpdateSystemLoad()
+{
+#if defined(LINUX) || defined(ANDROID)
+ nsCOMPtr<nsIFile> procStatFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ procStatFile->InitWithPath(NS_LITERAL_STRING("/proc/stat"));
+
+ nsCOMPtr<nsIInputStream> fileInputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream),
+ procStatFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString buffer;
+ bool isMore = true;
+ lineInputStream->ReadLine(buffer, &isMore);
+
+ uint64_t user;
+ uint64_t nice;
+ uint64_t system;
+ uint64_t idle;
+ if (PR_sscanf(buffer.get(), "cpu %llu %llu %llu %llu",
+ &user, &nice,
+ &system, &idle) != 4) {
+ LOG(("Error parsing /proc/stat"));
+ return NS_ERROR_FAILURE;
+ }
+
+ const uint64_t cpu_times = nice + system + user;
+ const uint64_t total_times = cpu_times + idle;
+
+ UpdateCpuLoad(mTicksPerInterval,
+ total_times,
+ cpu_times,
+ &mSystemLoad);
+ return NS_OK;
+#elif defined(XP_MACOSX)
+ mach_msg_type_number_t info_cnt = HOST_CPU_LOAD_INFO_COUNT;
+ host_cpu_load_info_data_t load_info;
+ kern_return_t rv = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
+ (host_info_t)(&load_info), &info_cnt);
+
+ if (rv != KERN_SUCCESS || info_cnt != HOST_CPU_LOAD_INFO_COUNT) {
+ LOG(("Error from mach/host_statistics call"));
+ return NS_ERROR_FAILURE;
+ }
+
+ const uint64_t cpu_times = load_info.cpu_ticks[CPU_STATE_NICE]
+ + load_info.cpu_ticks[CPU_STATE_SYSTEM]
+ + load_info.cpu_ticks[CPU_STATE_USER];
+ const uint64_t total_times = cpu_times + load_info.cpu_ticks[CPU_STATE_IDLE];
+
+ UpdateCpuLoad(mTicksPerInterval,
+ total_times,
+ cpu_times,
+ &mSystemLoad);
+ return NS_OK;
+#elif defined(__DragonFly__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__)
+#if defined(__NetBSD__)
+ uint64_t cp_time[CPUSTATES];
+#else
+ long cp_time[CPUSTATES];
+#endif // __NetBSD__
+ size_t sz = sizeof(cp_time);
+#ifdef KERN_CP_TIME
+ int mib[] = {
+ CTL_KERN,
+ KERN_CP_TIME,
+ };
+ u_int miblen = sizeof(mib) / sizeof(mib[0]);
+ if (sysctl(mib, miblen, &cp_time, &sz, nullptr, 0)) {
+#else
+ if (sysctlbyname("kern.cp_time", &cp_time, &sz, nullptr, 0)) {
+#endif // KERN_CP_TIME
+ LOG(("sysctl kern.cp_time failed"));
+ return NS_ERROR_FAILURE;
+ }
+
+ const uint64_t cpu_times = cp_time[CP_NICE]
+ + cp_time[CP_SYS]
+ + cp_time[CP_INTR]
+ + cp_time[CP_USER];
+ const uint64_t total_times = cpu_times + cp_time[CP_IDLE];
+
+ UpdateCpuLoad(mTicksPerInterval,
+ total_times,
+ cpu_times,
+ &mSystemLoad);
+ return NS_OK;
+#elif defined(XP_WIN)
+ float load;
+ nsresult rv = mSysMon.QuerySystemLoad(&load);
+
+ if (rv == NS_OK) {
+ mSystemLoad.mPrevLoad = load;
+ }
+
+ return rv;
+#else
+ // Not implemented
+ return NS_OK;
+#endif
+}
+
+nsresult RTCLoadInfo::UpdateProcessLoad() {
+#if defined(XP_UNIX)
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ const uint64_t total_times = tv.tv_sec * PR_USEC_PER_SEC + tv.tv_usec;
+
+ rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage) < 0) {
+ LOG(("getrusage failed"));
+ return NS_ERROR_FAILURE;
+ }
+
+ const uint64_t cpu_times =
+ (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * PR_USEC_PER_SEC +
+ usage.ru_utime.tv_usec + usage.ru_stime.tv_usec;
+
+ UpdateCpuLoad(PR_USEC_PER_MSEC * mLoadUpdateInterval,
+ total_times,
+ cpu_times,
+ &mProcessLoad);
+#elif defined(XP_WIN)
+ FILETIME clk_time, sys_time, user_time;
+ uint64_t total_times, cpu_times;
+
+ GetSystemTimeAsFileTime(&clk_time);
+ total_times = (((uint64_t)clk_time.dwHighDateTime) << 32)
+ + (uint64_t)clk_time.dwLowDateTime;
+ BOOL ok = GetProcessTimes(mProcHandle, &clk_time, &clk_time, &sys_time, &user_time);
+
+ if (ok == 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ cpu_times = (((uint64_t)sys_time.dwHighDateTime
+ + (uint64_t)user_time.dwHighDateTime) << 32)
+ + (uint64_t)sys_time.dwLowDateTime
+ + (uint64_t)user_time.dwLowDateTime;
+
+ UpdateCpuLoad(mTicksPerInterval,
+ total_times,
+ cpu_times,
+ &mProcessLoad);
+#endif
+ return NS_OK;
+}
+
+// Note: This class can't be in the anonymous namespace, because then we can't
+// declare it as a friend of LoadMonitor.
+class LoadInfoCollectRunner : public Runnable
+{
+public:
+ LoadInfoCollectRunner(RefPtr<LoadMonitor> loadMonitor,
+ RefPtr<RTCLoadInfo> loadInfo,
+ nsIThread *loadInfoThread)
+ : mThread(loadInfoThread),
+ mLoadUpdateInterval(loadMonitor->mLoadUpdateInterval),
+ mLoadNoiseCounter(0)
+ {
+ mLoadMonitor = loadMonitor;
+ mLoadInfo = loadInfo;
+ }
+
+ NS_IMETHOD Run() override
+ {
+ if (NS_IsMainThread()) {
+ if (mThread) {
+ // Don't leak threads!
+ mThread->Shutdown(); // can't Shutdown from the thread itself, darn
+ // Don't null out mThread!
+ // See bug 999104. We must hold a ref to the thread across Dispatch()
+ // since the internal mThread ref could be released while processing
+ // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
+ // assumes the caller does.
+ }
+ return NS_OK;
+ }
+
+ MutexAutoLock lock(mLoadMonitor->mLock);
+ while (!mLoadMonitor->mShutdownPending) {
+ mLoadInfo->UpdateSystemLoad();
+ mLoadInfo->UpdateProcessLoad();
+ float sysLoad = mLoadInfo->GetSystemLoad();
+ float procLoad = mLoadInfo->GetProcessLoad();
+
+ if ((++mLoadNoiseCounter % (LOG_MANY_ENABLED() ? 1 : 10)) == 0) {
+ LOG(("System Load: %f Process Load: %f", sysLoad, procLoad));
+ mLoadNoiseCounter = 0;
+ }
+ mLoadMonitor->SetSystemLoad(sysLoad);
+ mLoadMonitor->SetProcessLoad(procLoad);
+ mLoadMonitor->FireCallbacks();
+
+ mLoadMonitor->mCondVar.Wait(PR_MillisecondsToInterval(mLoadUpdateInterval));
+ }
+ // ok, we need to exit safely and can't shut ourselves down (DARN)
+ NS_DispatchToMainThread(this);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mThread;
+ RefPtr<RTCLoadInfo> mLoadInfo;
+ RefPtr<LoadMonitor> mLoadMonitor;
+ int mLoadUpdateInterval;
+ int mLoadNoiseCounter;
+};
+
+void
+LoadMonitor::SetProcessLoad(float load) {
+ mLock.AssertCurrentThreadOwns();
+ mProcessLoad = load;
+}
+
+void
+LoadMonitor::SetSystemLoad(float load) {
+ mLock.AssertCurrentThreadOwns();
+ mSystemLoad = load;
+}
+
+float
+LoadMonitor::GetProcessLoad() {
+ MutexAutoLock lock(mLock);
+ float load = mProcessLoad;
+ return load;
+}
+
+void
+LoadMonitor::FireCallbacks() {
+ if (mLoadNotificationCallback) {
+ mLoadNotificationCallback->LoadChanged(mSystemLoad, mProcessLoad);
+ }
+}
+
+float
+LoadMonitor::GetSystemLoad() {
+ MutexAutoLock lock(mLock);
+ float load = mSystemLoad;
+ return load;
+}
+
+nsresult
+LoadMonitor::Init(RefPtr<LoadMonitor> &self)
+{
+ LOG(("Initializing LoadMonitor"));
+
+ RefPtr<RTCLoadInfo> load_info = new RTCLoadInfo();
+ nsresult rv = load_info->Init(mLoadUpdateInterval);
+
+ if (NS_FAILED(rv)) {
+ LOG(("RTCLoadInfo::Init error"));
+ return rv;
+ }
+
+ RefPtr<LoadMonitorAddObserver> addObsRunner = new LoadMonitorAddObserver(self);
+ NS_DispatchToMainThread(addObsRunner);
+
+ NS_NewNamedThread("Sys Load Info", getter_AddRefs(mLoadInfoThread));
+
+ RefPtr<LoadInfoCollectRunner> runner =
+ new LoadInfoCollectRunner(self, load_info, mLoadInfoThread);
+ mLoadInfoThread->Dispatch(runner, NS_DISPATCH_NORMAL);
+
+ return NS_OK;
+}
+
+void
+LoadMonitor::SetLoadChangeCallback(LoadNotificationCallback* aCallback)
+{
+ mLoadNotificationCallback = aCallback;
+}
+
+}
diff --git a/dom/media/systemservices/LoadMonitor.h b/dom/media/systemservices/LoadMonitor.h
new file mode 100644
index 000000000..3731b97dc
--- /dev/null
+++ b/dom/media/systemservices/LoadMonitor.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 _LOADMONITOR_H_
+#define _LOADMONITOR_H_
+
+#include "mozilla/Mutex.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Atomics.h"
+#include "nsCOMPtr.h"
+#include "nsIThread.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+class LoadInfoCollectRunner;
+
+class LoadNotificationCallback
+{
+public:
+ virtual void LoadChanged(float aSystemLoad, float aProcessLoad) = 0;
+};
+
+class LoadMonitor final : public nsIObserver
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit LoadMonitor(int aLoadUpdateInterval);
+
+ nsresult Init(RefPtr<LoadMonitor> &self);
+ void SetLoadChangeCallback(LoadNotificationCallback* aCallback);
+ void Shutdown();
+ float GetSystemLoad();
+ float GetProcessLoad();
+
+ friend class LoadInfoCollectRunner;
+
+private:
+ ~LoadMonitor();
+
+ void SetProcessLoad(float load);
+ void SetSystemLoad(float load);
+ void FireCallbacks();
+
+ int mLoadUpdateInterval;
+ mozilla::Mutex mLock;
+ mozilla::CondVar mCondVar;
+ bool mShutdownPending;
+ nsCOMPtr<nsIThread> mLoadInfoThread;
+ float mSystemLoad;
+ float mProcessLoad;
+ LoadNotificationCallback* mLoadNotificationCallback;
+};
+
+} //namespace
+
+#endif /* _LOADMONITOR_H_ */
diff --git a/dom/media/systemservices/MediaChild.cpp b/dom/media/systemservices/MediaChild.cpp
new file mode 100644
index 000000000..327ea3c4a
--- /dev/null
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -0,0 +1,115 @@
+/* -*- 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/. */
+
+#include "MediaChild.h"
+#include "MediaParent.h"
+
+#include "nsGlobalWindow.h"
+#include "mozilla/MediaManager.h"
+#include "mozilla/Logging.h"
+#include "nsQueryObject.h"
+
+#undef LOG
+mozilla::LazyLogModule gMediaChildLog("MediaChild");
+#define LOG(args) MOZ_LOG(gMediaChildLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla {
+namespace media {
+
+already_AddRefed<Pledge<nsCString>>
+GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing, bool aPersist)
+{
+ RefPtr<MediaManager> mgr = MediaManager::GetInstance();
+ MOZ_ASSERT(mgr);
+
+ RefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
+ uint32_t id = mgr->mGetOriginKeyPledges.Append(*p);
+
+ if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing,
+ aPersist);
+ } else {
+ Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing, aPersist);
+ }
+ return p.forget();
+}
+
+void
+SanitizeOriginKeys(const uint64_t& aSinceWhen, bool aOnlyPrivateBrowsing)
+{
+ LOG(("SanitizeOriginKeys since %llu %s", aSinceWhen,
+ (aOnlyPrivateBrowsing? "in Private Browsing." : ".")));
+
+ if (XRE_GetProcessType() == GeckoProcessType_Default) {
+ // Avoid opening MediaManager in this case, since this is called by
+ // sanitize.js when cookies are cleared, which can happen on startup.
+ RefPtr<Parent<NonE10s>> tmpParent = new Parent<NonE10s>();
+ tmpParent->RecvSanitizeOriginKeys(aSinceWhen, aOnlyPrivateBrowsing);
+ } else {
+ Child::Get()->SendSanitizeOriginKeys(aSinceWhen, aOnlyPrivateBrowsing);
+ }
+}
+
+static Child* sChild;
+
+Child* Child::Get()
+{
+ MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sChild) {
+ sChild = static_cast<Child*>(dom::ContentChild::GetSingleton()->SendPMediaConstructor());
+ }
+ return sChild;
+}
+
+Child::Child()
+ : mActorDestroyed(false)
+{
+ LOG(("media::Child: %p", this));
+ MOZ_COUNT_CTOR(Child);
+}
+
+Child::~Child()
+{
+ LOG(("~media::Child: %p", this));
+ sChild = nullptr;
+ MOZ_COUNT_DTOR(Child);
+}
+
+void Child::ActorDestroy(ActorDestroyReason aWhy)
+{
+ mActorDestroyed = true;
+}
+
+bool
+Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
+{
+ RefPtr<MediaManager> mgr = MediaManager::GetInstance();
+ if (!mgr) {
+ return false;
+ }
+ RefPtr<Pledge<nsCString>> pledge = mgr->mGetOriginKeyPledges.Remove(aRequestId);
+ if (pledge) {
+ pledge->Resolve(aKey);
+ }
+ return true;
+}
+
+PMediaChild*
+AllocPMediaChild()
+{
+ return new Child();
+}
+
+bool
+DeallocPMediaChild(media::PMediaChild *aActor)
+{
+ delete static_cast<Child*>(aActor);
+ return true;
+}
+
+} // namespace media
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaChild.h b/dom/media/systemservices/MediaChild.h
new file mode 100644
index 000000000..b013c4a5f
--- /dev/null
+++ b/dom/media/systemservices/MediaChild.h
@@ -0,0 +1,55 @@
+/* -*- 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_MediaChild_h
+#define mozilla_MediaChild_h
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/media/PMediaChild.h"
+#include "mozilla/media/PMediaParent.h"
+#include "MediaUtils.h"
+
+namespace mozilla {
+namespace media {
+
+// media::Child implements proxying to the chrome process for some media-related
+// functions, for the moment just:
+//
+// GetOriginKey() - get a cookie-like persisted unique key for a given origin.
+// SanitizeOriginKeys() - reset persisted unique keys.
+
+// GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
+// (promise-like objects) with the future value. Use pledge.Then(func) to access.
+
+already_AddRefed<Pledge<nsCString>>
+GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing, bool aPersist);
+
+void
+SanitizeOriginKeys(const uint64_t& aSinceWhen, bool aOnlyPrivateBrowsing);
+
+class Child : public PMediaChild
+{
+public:
+ static Child* Get();
+
+ Child();
+
+ bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+ virtual ~Child();
+private:
+
+ bool mActorDestroyed;
+};
+
+PMediaChild* AllocPMediaChild();
+bool DeallocPMediaChild(PMediaChild *aActor);
+
+} // namespace media
+} // namespace mozilla
+
+#endif // mozilla_MediaChild_h
diff --git a/dom/media/systemservices/MediaParent.cpp b/dom/media/systemservices/MediaParent.cpp
new file mode 100644
index 000000000..109a44a28
--- /dev/null
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -0,0 +1,516 @@
+/* -*- 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/. */
+
+#include "MediaParent.h"
+
+#include "mozilla/Base64.h"
+#include <mozilla/StaticMutex.h>
+
+#include "MediaUtils.h"
+#include "MediaEngine.h"
+#include "VideoUtils.h"
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsISafeOutputStream.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/Logging.h"
+
+#undef LOG
+mozilla::LazyLogModule gMediaParentLog("MediaParent");
+#define LOG(args) MOZ_LOG(gMediaParentLog, mozilla::LogLevel::Debug, args)
+
+// A file in the profile dir is used to persist mOriginKeys used to anonymize
+// deviceIds to be unique per origin, to avoid them being supercookies.
+
+#define ORIGINKEYS_FILE "enumerate_devices.txt"
+#define ORIGINKEYS_VERSION "1"
+
+namespace mozilla {
+namespace media {
+
+static OriginKeyStore* sOriginKeyStore = nullptr;
+
+class OriginKeyStore : public nsISupports
+{
+ NS_DECL_THREADSAFE_ISUPPORTS
+ class OriginKey
+ {
+ public:
+ static const size_t DecodedLength = 18;
+ static const size_t EncodedLength = DecodedLength * 4 / 3;
+
+ explicit OriginKey(const nsACString& aKey, int64_t aSecondsStamp = 0) // 0 = temporal
+ : mKey(aKey)
+ , mSecondsStamp(aSecondsStamp) {}
+
+ nsCString mKey; // Base64 encoded.
+ int64_t mSecondsStamp;
+ };
+
+ class OriginKeysTable
+ {
+ public:
+ OriginKeysTable() : mPersistCount(0) {}
+
+ nsresult
+ GetOriginKey(const nsACString& aOrigin, nsCString& aResult, bool aPersist = false)
+ {
+ OriginKey* key;
+ if (!mKeys.Get(aOrigin, &key)) {
+ nsCString salt; // Make a new one
+ nsresult rv = GenerateRandomName(salt, key->EncodedLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ key = new OriginKey(salt);
+ mKeys.Put(aOrigin, key);
+ }
+ if (aPersist && !key->mSecondsStamp) {
+ key->mSecondsStamp = PR_Now() / PR_USEC_PER_SEC;
+ mPersistCount++;
+ }
+ aResult = key->mKey;
+ return NS_OK;
+ }
+
+ void Clear(int64_t aSinceWhen)
+ {
+ // Avoid int64_t* <-> void* casting offset
+ OriginKey since(nsCString(), aSinceWhen / PR_USEC_PER_SEC);
+ for (auto iter = mKeys.Iter(); !iter.Done(); iter.Next()) {
+ nsAutoPtr<OriginKey>& originKey = iter.Data();
+ LOG((((originKey->mSecondsStamp >= since.mSecondsStamp)?
+ "%s: REMOVE %lld >= %lld" :
+ "%s: KEEP %lld < %lld"),
+ __FUNCTION__, originKey->mSecondsStamp, since.mSecondsStamp));
+
+ if (originKey->mSecondsStamp >= since.mSecondsStamp) {
+ iter.Remove();
+ }
+ }
+ mPersistCount = 0;
+ }
+
+ protected:
+ nsClassHashtable<nsCStringHashKey, OriginKey> mKeys;
+ size_t mPersistCount;
+ };
+
+ class OriginKeysLoader : public OriginKeysTable
+ {
+ public:
+ OriginKeysLoader() {}
+
+ nsresult
+ GetOriginKey(const nsACString& aOrigin, nsCString& aResult, bool aPersist)
+ {
+ auto before = mPersistCount;
+ OriginKeysTable::GetOriginKey(aOrigin, aResult, aPersist);
+ if (mPersistCount != before) {
+ Save();
+ }
+ return NS_OK;
+ }
+
+ already_AddRefed<nsIFile>
+ GetFile()
+ {
+ MOZ_ASSERT(mProfileDir);
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+ file->Append(NS_LITERAL_STRING(ORIGINKEYS_FILE));
+ return file.forget();
+ }
+
+ // Format of file is key secondsstamp origin (first line is version #):
+ //
+ // 1
+ // rOMAAbFujNwKyIpj4RJ3Wt5Q 1424733961 http://fiddle.jshell.net
+ // rOMAAbFujNwKyIpj4RJ3Wt5Q 1424734841 http://mozilla.github.io
+ // etc.
+
+ nsresult Read()
+ {
+ nsCOMPtr<nsIFile> file = GetFile();
+ if (NS_WARN_IF(!file)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ bool exists;
+ nsresult rv = file->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (!exists) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIInputStream> stream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ nsCOMPtr<nsILineInputStream> i = do_QueryInterface(stream);
+ MOZ_ASSERT(i);
+ MOZ_ASSERT(!mPersistCount);
+
+ nsCString line;
+ bool hasMoreLines;
+ rv = i->ReadLine(line, &hasMoreLines);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (!line.EqualsLiteral(ORIGINKEYS_VERSION)) {
+ // If version on disk is newer than we can understand then ignore it.
+ return NS_OK;
+ }
+
+ while (hasMoreLines) {
+ rv = i->ReadLine(line, &hasMoreLines);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ // Read key secondsstamp origin.
+ // Ignore any lines that don't fit format in the comment above exactly.
+ int32_t f = line.FindChar(' ');
+ if (f < 0) {
+ continue;
+ }
+ const nsACString& key = Substring(line, 0, f);
+ const nsACString& s = Substring(line, f+1);
+ f = s.FindChar(' ');
+ if (f < 0) {
+ continue;
+ }
+ int64_t secondsstamp = nsCString(Substring(s, 0, f)).ToInteger64(&rv);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+ const nsACString& origin = Substring(s, f+1);
+
+ // Validate key
+ if (key.Length() != OriginKey::EncodedLength) {
+ continue;
+ }
+ nsCString dummy;
+ rv = Base64Decode(key, dummy);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+ mKeys.Put(origin, new OriginKey(key, secondsstamp));
+ }
+ mPersistCount = mKeys.Count();
+ return NS_OK;
+ }
+
+ nsresult
+ Write()
+ {
+ nsCOMPtr<nsIFile> file = GetFile();
+ if (NS_WARN_IF(!file)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIOutputStream> stream;
+ nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoCString versionBuffer;
+ versionBuffer.AppendLiteral(ORIGINKEYS_VERSION);
+ versionBuffer.Append('\n');
+
+ uint32_t count;
+ rv = stream->Write(versionBuffer.Data(), versionBuffer.Length(), &count);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ if (count != versionBuffer.Length()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ for (auto iter = mKeys.Iter(); !iter.Done(); iter.Next()) {
+ const nsACString& origin = iter.Key();
+ OriginKey* originKey = iter.UserData();
+
+ if (!originKey->mSecondsStamp) {
+ continue; // don't write temporal ones
+ }
+
+ nsCString originBuffer;
+ originBuffer.Append(originKey->mKey);
+ originBuffer.Append(' ');
+ originBuffer.AppendInt(originKey->mSecondsStamp);
+ originBuffer.Append(' ');
+ originBuffer.Append(origin);
+ originBuffer.Append('\n');
+
+ rv = stream->Write(originBuffer.Data(), originBuffer.Length(), &count);
+ if (NS_WARN_IF(NS_FAILED(rv)) || count != originBuffer.Length()) {
+ break;
+ }
+ }
+
+ nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
+ MOZ_ASSERT(safeStream);
+
+ rv = safeStream->Finish();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ nsresult Load()
+ {
+ nsresult rv = Read();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ Delete();
+ }
+ return rv;
+ }
+
+ nsresult Save()
+ {
+ nsresult rv = Write();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ NS_WARNING("Failed to write data for EnumerateDevices id-persistence.");
+ Delete();
+ }
+ return rv;
+ }
+
+ void Clear(int64_t aSinceWhen)
+ {
+ OriginKeysTable::Clear(aSinceWhen);
+ Delete();
+ Save();
+ }
+
+ nsresult Delete()
+ {
+ nsCOMPtr<nsIFile> file = GetFile();
+ if (NS_WARN_IF(!file)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ nsresult rv = file->Remove(false);
+ if (rv == NS_ERROR_FILE_NOT_FOUND) {
+ return NS_OK;
+ }
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ void
+ SetProfileDir(nsIFile* aProfileDir)
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ bool first = !mProfileDir;
+ mProfileDir = aProfileDir;
+ // Load from disk when we first get a profileDir, but not subsequently.
+ if (first) {
+ Load();
+ }
+ }
+ private:
+ nsCOMPtr<nsIFile> mProfileDir;
+ };
+
+private:
+ virtual ~OriginKeyStore()
+ {
+ sOriginKeyStore = nullptr;
+ LOG((__FUNCTION__));
+ }
+
+public:
+ static OriginKeyStore* Get()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sOriginKeyStore) {
+ sOriginKeyStore = new OriginKeyStore();
+ }
+ return sOriginKeyStore;
+ }
+
+ // Only accessed on StreamTS thread
+ OriginKeysLoader mOriginKeys;
+ OriginKeysTable mPrivateBrowsingOriginKeys;
+};
+
+NS_IMPL_ISUPPORTS0(OriginKeyStore)
+
+bool NonE10s::SendGetOriginKeyResponse(const uint32_t& aRequestId,
+ nsCString aKey) {
+ MediaManager* mgr = MediaManager::GetIfExists();
+ if (!mgr) {
+ return false;
+ }
+ RefPtr<Pledge<nsCString>> pledge = mgr->mGetOriginKeyPledges.Remove(aRequestId);
+ if (pledge) {
+ pledge->Resolve(aKey);
+ }
+ return true;
+}
+
+template<class Super> bool
+Parent<Super>::RecvGetOriginKey(const uint32_t& aRequestId,
+ const nsCString& aOrigin,
+ const bool& aPrivateBrowsing,
+ const bool& aPersist)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // First, get profile dir.
+
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIFile> profileDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ // Then over to stream-transport thread to do the actual file io.
+ // Stash a pledge to hold the answer and get an id for this request.
+
+ RefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
+ uint32_t id = mOutstandingPledges.Append(*p);
+
+ nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ MOZ_ASSERT(sts);
+ RefPtr<Parent<Super>> that(this);
+
+ rv = sts->Dispatch(NewRunnableFrom([this, that, id, profileDir, aOrigin,
+ aPrivateBrowsing, aPersist]() -> nsresult {
+ MOZ_ASSERT(!NS_IsMainThread());
+ mOriginKeyStore->mOriginKeys.SetProfileDir(profileDir);
+ nsCString result;
+ if (aPrivateBrowsing) {
+ mOriginKeyStore->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
+ } else {
+ mOriginKeyStore->mOriginKeys.GetOriginKey(aOrigin, result, aPersist);
+ }
+
+ // Pass result back to main thread.
+ nsresult rv;
+ rv = NS_DispatchToMainThread(NewRunnableFrom([this, that, id,
+ result]() -> nsresult {
+ if (mDestroyed) {
+ return NS_OK;
+ }
+ RefPtr<Pledge<nsCString>> p = mOutstandingPledges.Remove(id);
+ if (!p) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ p->Resolve(result);
+ return NS_OK;
+ }), NS_DISPATCH_NORMAL);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+ }), NS_DISPATCH_NORMAL);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ p->Then([this, that, aRequestId](const nsCString& aKey) mutable {
+ if (mDestroyed) {
+ return NS_OK;
+ }
+ Unused << this->SendGetOriginKeyResponse(aRequestId, aKey);
+ return NS_OK;
+ });
+ return true;
+}
+
+template<class Super> bool
+Parent<Super>::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen,
+ const bool& aOnlyPrivateBrowsing)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIFile> profileDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ // Over to stream-transport thread to do the file io.
+
+ nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ MOZ_ASSERT(sts);
+ RefPtr<OriginKeyStore> store(mOriginKeyStore);
+
+ rv = sts->Dispatch(NewRunnableFrom([profileDir, store, aSinceWhen,
+ aOnlyPrivateBrowsing]() -> nsresult {
+ MOZ_ASSERT(!NS_IsMainThread());
+ store->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
+ if (!aOnlyPrivateBrowsing) {
+ store->mOriginKeys.SetProfileDir(profileDir);
+ store->mOriginKeys.Clear(aSinceWhen);
+ }
+ return NS_OK;
+ }), NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ return true;
+}
+
+template<class Super> void
+Parent<Super>::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // No more IPC from here
+ mDestroyed = true;
+ LOG((__FUNCTION__));
+}
+
+template<class Super>
+Parent<Super>::Parent()
+ : mOriginKeyStore(OriginKeyStore::Get())
+ , mDestroyed(false)
+{
+ LOG(("media::Parent: %p", this));
+}
+
+template<class Super>
+Parent<Super>::~Parent()
+{
+ LOG(("~media::Parent: %p", this));
+}
+
+PMediaParent*
+AllocPMediaParent()
+{
+ Parent<PMediaParent>* obj = new Parent<PMediaParent>();
+ obj->AddRef();
+ return obj;
+}
+
+bool
+DeallocPMediaParent(media::PMediaParent *aActor)
+{
+ static_cast<Parent<PMediaParent>*>(aActor)->Release();
+ return true;
+}
+
+} // namespace media
+} // namespace mozilla
+
+// Instantiate templates to satisfy linker
+template class mozilla::media::Parent<mozilla::media::NonE10s>;
diff --git a/dom/media/systemservices/MediaParent.h b/dom/media/systemservices/MediaParent.h
new file mode 100644
index 000000000..b5dcd84ad
--- /dev/null
+++ b/dom/media/systemservices/MediaParent.h
@@ -0,0 +1,77 @@
+/* -*- 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_MediaParent_h
+#define mozilla_MediaParent_h
+
+#include "MediaChild.h"
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/media/PMediaParent.h"
+
+namespace mozilla {
+namespace media {
+
+// media::Parent implements the chrome-process side of ipc for media::Child APIs
+// A same-process version may also be created to service non-e10s calls.
+
+class OriginKeyStore;
+
+class NonE10s
+{
+ typedef mozilla::ipc::IProtocol::ActorDestroyReason
+ ActorDestroyReason;
+public:
+ virtual ~NonE10s() {}
+protected:
+ virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
+ const nsCString& aOrigin,
+ const bool& aPrivateBrowsing,
+ const bool& aPersist) = 0;
+ virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen,
+ const bool& aOnlyPrivateBrowsing) = 0;
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) = 0;
+
+ bool SendGetOriginKeyResponse(const uint32_t& aRequestId,
+ nsCString aKey);
+};
+
+// Super = PMediaParent or NonE10s
+
+template<class Super>
+class Parent : public Super
+{
+ typedef mozilla::ipc::IProtocol::ActorDestroyReason
+ ActorDestroyReason;
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Parent<Super>)
+
+ virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
+ const nsCString& aOrigin,
+ const bool& aPrivateBrowsing,
+ const bool& aPersist) override;
+ virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen,
+ const bool& aOnlyPrivateBrowsing) override;
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ Parent();
+private:
+ virtual ~Parent();
+
+ RefPtr<OriginKeyStore> mOriginKeyStore;
+ bool mDestroyed;
+
+ CoatCheck<Pledge<nsCString>> mOutstandingPledges;
+};
+
+PMediaParent* AllocPMediaParent();
+bool DeallocPMediaParent(PMediaParent *aActor);
+
+} // namespace media
+} // namespace mozilla
+
+#endif // mozilla_MediaParent_h
diff --git a/dom/media/systemservices/MediaSystemResourceClient.cpp b/dom/media/systemservices/MediaSystemResourceClient.cpp
new file mode 100644
index 000000000..e1ad38aef
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceClient.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "mozilla/Monitor.h"
+#include "mozilla/ReentrantMonitor.h"
+
+#include "MediaSystemResourceClient.h"
+
+namespace mozilla {
+
+Atomic<uint32_t> MediaSystemResourceClient::sSerialCounter(0);
+
+MediaSystemResourceClient::MediaSystemResourceClient(MediaSystemResourceType aReourceType)
+ : mResourceType(aReourceType)
+ , mId(++sSerialCounter)
+ , mListener(nullptr)
+ , mResourceState(RESOURCE_STATE_START)
+ , mIsSync(false)
+ , mAcquireSyncWaitMonitor(nullptr)
+ , mAcquireSyncWaitDone(nullptr)
+{
+ mManager = MediaSystemResourceManager::Get();
+ if (mManager) {
+ mManager->Register(this);
+ }
+}
+
+MediaSystemResourceClient::~MediaSystemResourceClient()
+{
+ ReleaseResource();
+ if (mManager) {
+ mManager->Unregister(this);
+ }
+}
+
+bool
+MediaSystemResourceClient::SetListener(MediaSystemResourceReservationListener* aListener)
+{
+ if (!mManager) {
+ return false;
+ }
+ return mManager->SetListener(this, aListener);
+}
+
+void
+MediaSystemResourceClient::Acquire()
+{
+ if (!mManager) {
+ return;
+ }
+ mManager->Acquire(this);
+}
+
+bool
+MediaSystemResourceClient::AcquireSyncNoWait()
+{
+ if (!mManager) {
+ return false;
+ }
+ return mManager->AcquireSyncNoWait(this);
+}
+
+void
+MediaSystemResourceClient::ReleaseResource()
+{
+ if (!mManager) {
+ return;
+ }
+ mManager->ReleaseResource(this);
+}
+
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaSystemResourceClient.h b/dom/media/systemservices/MediaSystemResourceClient.h
new file mode 100644
index 000000000..29e769a92
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceClient.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(MediaSystemResourceClient_h_)
+#define MediaSystemResourceClient_h_
+
+#include "MediaSystemResourceManager.h"
+#include "MediaSystemResourceTypes.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/media/MediaSystemResourceTypes.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+
+class MediaSystemResourceManager;
+
+
+/**
+ * This is a base class for listener callbacks.
+ * This callback is invoked when the media system resource reservation state
+ * is changed.
+ */
+class MediaSystemResourceReservationListener {
+public:
+ virtual void ResourceReserved() = 0;
+ virtual void ResourceReserveFailed() = 0;
+};
+
+/**
+ * MediaSystemResourceClient is used to reserve a media system resource
+ * like hw decoder. When system has a limitation of a media resource,
+ * use this class to mediate use rights of the resource.
+ */
+class MediaSystemResourceClient
+{
+public:
+
+ // Enumeration for the valid decoding states
+ enum ResourceState {
+ RESOURCE_STATE_START,
+ RESOURCE_STATE_WAITING,
+ RESOURCE_STATE_ACQUIRED,
+ RESOURCE_STATE_NOT_ACQUIRED,
+ RESOURCE_STATE_END
+ };
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSystemResourceClient)
+
+ explicit MediaSystemResourceClient(MediaSystemResourceType aReourceType);
+
+ bool SetListener(MediaSystemResourceReservationListener* aListener);
+
+ // Try to acquire media resource asynchronously.
+ // If the resource is used by others, wait until acquired.
+ void Acquire();
+
+ // Try to acquire media resource synchronously. If the resource is not immediately
+ // available, fail to acquire it.
+ // return false if resource is not acquired.
+ // return true if resource is acquired.
+ //
+ // This function should not be called on ImageBridge thread.
+ // It should be used only for compatibility with legacy code.
+ bool AcquireSyncNoWait();
+
+ void ReleaseResource();
+
+private:
+ ~MediaSystemResourceClient();
+
+ RefPtr<MediaSystemResourceManager> mManager;
+ const MediaSystemResourceType mResourceType;
+ const uint32_t mId;
+
+ // Modified only by MediaSystemResourceManager.
+ // Accessed and modified with MediaSystemResourceManager::mReentrantMonitor held.
+ MediaSystemResourceReservationListener* mListener;
+ ResourceState mResourceState;
+ bool mIsSync;
+ ReentrantMonitor* mAcquireSyncWaitMonitor;
+ bool* mAcquireSyncWaitDone;
+
+ static mozilla::Atomic<uint32_t> sSerialCounter;
+
+ friend class MediaSystemResourceManager;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/systemservices/MediaSystemResourceManager.cpp b/dom/media/systemservices/MediaSystemResourceManager.cpp
new file mode 100644
index 000000000..29db0ef3e
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceManager.cpp
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "mozilla/TaskQueue.h"
+
+#include "gfxPrefs.h"
+#include "MediaSystemResourceManagerChild.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+
+#include "MediaSystemResourceManager.h"
+
+namespace mozilla {
+
+using namespace mozilla::ipc;
+using namespace mozilla::layers;
+
+/* static */ StaticRefPtr<MediaSystemResourceManager> MediaSystemResourceManager::sSingleton;
+
+/* static */ MediaSystemResourceManager*
+MediaSystemResourceManager::Get()
+{
+ if (sSingleton) {
+ return sSingleton;
+ }
+ MediaSystemResourceManager::Init();
+ return sSingleton;
+}
+
+/* static */ void
+MediaSystemResourceManager::Shutdown()
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (sSingleton) {
+ sSingleton->CloseIPC();
+ sSingleton = nullptr;
+ }
+}
+
+/* static */ void
+MediaSystemResourceManager::Init()
+{
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ if (!imageBridge) {
+ NS_WARNING("ImageBridge does not exist");
+ return;
+ }
+
+ if (InImageBridgeChildThread()) {
+ if (!sSingleton) {
+#ifdef DEBUG
+ static int timesCreated = 0;
+ timesCreated++;
+ MOZ_ASSERT(timesCreated == 1);
+#endif
+ sSingleton = new MediaSystemResourceManager();
+ }
+ return;
+ }
+
+ ReentrantMonitor barrier("MediaSystemResourceManager::Init");
+ ReentrantMonitorAutoEnter mainThreadAutoMon(barrier);
+ bool done = false;
+
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ if (!sSingleton) {
+ sSingleton = new MediaSystemResourceManager();
+ }
+ ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
+ done = true;
+ barrier.NotifyAll();
+ });
+
+ imageBridge->GetMessageLoop()->PostTask(runnable.forget());
+
+ // should stop the thread until done.
+ while (!done) {
+ barrier.Wait();
+ }
+}
+
+MediaSystemResourceManager::MediaSystemResourceManager()
+ : mReentrantMonitor("MediaSystemResourceManager.mReentrantMonitor")
+ , mShutDown(false)
+ , mChild(nullptr)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ OpenIPC();
+}
+
+MediaSystemResourceManager::~MediaSystemResourceManager()
+{
+ MOZ_ASSERT(IsIpcClosed());
+}
+
+void
+MediaSystemResourceManager::OpenIPC()
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ MOZ_ASSERT(!mChild);
+
+ media::PMediaSystemResourceManagerChild* child =
+ ImageBridgeChild::GetSingleton()->SendPMediaSystemResourceManagerConstructor();
+ mChild = static_cast<media::MediaSystemResourceManagerChild*>(child);
+ mChild->SetManager(this);
+}
+
+void
+MediaSystemResourceManager::CloseIPC()
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+
+ if (!mChild) {
+ return;
+ }
+ mChild->Destroy();
+ mChild = nullptr;
+ mShutDown = true;
+}
+
+void
+MediaSystemResourceManager::OnIpcClosed()
+{
+ mChild = nullptr;
+}
+
+bool
+MediaSystemResourceManager::IsIpcClosed()
+{
+ return mChild ? true : false;
+}
+
+void
+MediaSystemResourceManager::Register(MediaSystemResourceClient* aClient)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(!mResourceClients.Get(aClient->mId));
+
+ mResourceClients.Put(aClient->mId, aClient);
+}
+
+void
+MediaSystemResourceManager::Unregister(MediaSystemResourceClient* aClient)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(mResourceClients.Get(aClient->mId));
+ MOZ_ASSERT(mResourceClients.Get(aClient->mId) == aClient);
+
+ mResourceClients.Remove(aClient->mId);
+}
+
+bool
+MediaSystemResourceManager::SetListener(MediaSystemResourceClient* aClient,
+ MediaSystemResourceReservationListener* aListener)
+{
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MOZ_ASSERT(aClient);
+
+ MediaSystemResourceClient* client = mResourceClients.Get(aClient->mId);
+ MOZ_ASSERT(client);
+
+ if (!client) {
+ return false;
+ }
+ // State Check
+ if (aClient->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_START) {
+ return false;
+ }
+ aClient->mListener = aListener;
+ return true;
+}
+
+void
+MediaSystemResourceManager::Acquire(MediaSystemResourceClient* aClient)
+{
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(!InImageBridgeChildThread());
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MediaSystemResourceClient* client = mResourceClients.Get(aClient->mId);
+ MOZ_ASSERT(client);
+ MOZ_ASSERT(client == aClient);
+
+ aClient->mIsSync = false; // async request
+
+ if (!client) {
+ HandleAcquireResult(aClient->mId, false);
+ return;
+ }
+ // State Check
+ if (aClient->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_START) {
+ HandleAcquireResult(aClient->mId, false);
+ return;
+ }
+ aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_WAITING;
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
+ NewRunnableMethod<uint32_t>(
+ this,
+ &MediaSystemResourceManager::DoAcquire,
+ aClient->mId));
+}
+
+bool
+MediaSystemResourceManager::AcquireSyncNoWait(MediaSystemResourceClient* aClient)
+{
+ MOZ_ASSERT(aClient);
+ MOZ_ASSERT(!InImageBridgeChildThread());
+
+ ReentrantMonitor barrier("MediaSystemResourceManager::AcquireSyncNoWait");
+ ReentrantMonitorAutoEnter autoMon(barrier);
+ bool done = false;
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MediaSystemResourceClient* client = mResourceClients.Get(aClient->mId);
+ MOZ_ASSERT(client);
+ MOZ_ASSERT(client == aClient);
+
+ aClient->mIsSync = true; // sync request
+
+ if (InImageBridgeChildThread()) {
+ HandleAcquireResult(aClient->mId, false);
+ return false;
+ }
+ if (!client || client != aClient) {
+ HandleAcquireResult(aClient->mId, false);
+ return false;
+ }
+ // State Check
+ if (aClient->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_START) {
+ HandleAcquireResult(aClient->mId, false);
+ return false;
+ }
+ // Hold barrier Monitor until acquire task end.
+ aClient->mAcquireSyncWaitMonitor = &barrier;
+ aClient->mAcquireSyncWaitDone = &done;
+ aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_WAITING;
+ }
+
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
+ NewRunnableMethod<uint32_t>(
+ this,
+ &MediaSystemResourceManager::DoAcquire,
+ aClient->mId));
+
+ // should stop the thread until done.
+ while (!done) {
+ barrier.Wait();
+ }
+
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ if (aClient->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_ACQUIRED) {
+ return false;
+ }
+ return true;
+ }
+}
+
+void
+MediaSystemResourceManager::DoAcquire(uint32_t aId)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (mShutDown || !mChild) {
+ HandleAcquireResult(aId, false);
+ return;
+ }
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MediaSystemResourceClient* client = mResourceClients.Get(aId);
+ MOZ_ASSERT(client);
+
+ if (!client ||
+ client->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_WAITING) {
+ HandleAcquireResult(aId, false);
+ return;
+ }
+ MOZ_ASSERT(aId == client->mId);
+ bool willWait = !client->mAcquireSyncWaitMonitor ? true : false;
+ mChild->SendAcquire(client->mId,
+ client->mResourceType,
+ willWait);
+ }
+}
+
+void
+MediaSystemResourceManager::ReleaseResource(MediaSystemResourceClient* aClient)
+{
+ MOZ_ASSERT(aClient);
+ {
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MediaSystemResourceClient* client = mResourceClients.Get(aClient->mId);
+ MOZ_ASSERT(client);
+ MOZ_ASSERT(client == aClient);
+
+ if (!client ||
+ client != aClient ||
+ aClient->mResourceState == MediaSystemResourceClient::RESOURCE_STATE_START ||
+ aClient->mResourceState == MediaSystemResourceClient::RESOURCE_STATE_END) {
+
+ aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_END;
+ return;
+ }
+
+ aClient->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_END;
+
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
+ NewRunnableMethod<uint32_t>(
+ this,
+ &MediaSystemResourceManager::DoRelease,
+ aClient->mId));
+ }
+}
+
+void
+MediaSystemResourceManager::DoRelease(uint32_t aId)
+{
+ MOZ_ASSERT(InImageBridgeChildThread());
+ if (mShutDown || !mChild) {
+ return;
+ }
+ mChild->SendRelease(aId);
+}
+
+void
+MediaSystemResourceManager::RecvResponse(uint32_t aId, bool aSuccess)
+{
+ HandleAcquireResult(aId, aSuccess);
+}
+
+void
+MediaSystemResourceManager::HandleAcquireResult(uint32_t aId, bool aSuccess)
+{
+ if (!InImageBridgeChildThread()) {
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
+ NewRunnableMethod<uint32_t, bool>(
+ this,
+ &MediaSystemResourceManager::HandleAcquireResult,
+ aId,
+ aSuccess));
+ return;
+ }
+
+ ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+ MediaSystemResourceClient* client = mResourceClients.Get(aId);
+ if (!client) {
+ // Client was already unregistered.
+ return;
+ }
+ if (client->mResourceState != MediaSystemResourceClient::RESOURCE_STATE_WAITING) {
+ return;
+ }
+
+ // Update state
+ if (aSuccess) {
+ client->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_ACQUIRED;
+ } else {
+ client->mResourceState = MediaSystemResourceClient::RESOURCE_STATE_NOT_ACQUIRED;
+ }
+
+ if (client->mIsSync) {
+ if (client->mAcquireSyncWaitMonitor) {
+ // Notify AcquireSync() complete
+ MOZ_ASSERT(client->mAcquireSyncWaitDone);
+ ReentrantMonitorAutoEnter autoMon(*client->mAcquireSyncWaitMonitor);
+ *client->mAcquireSyncWaitDone = true;
+ client->mAcquireSyncWaitMonitor->NotifyAll();
+ client->mAcquireSyncWaitMonitor = nullptr;
+ client->mAcquireSyncWaitDone = nullptr;
+ }
+ } else {
+ // Notify Acquire() result
+ if (client->mListener) {
+ if (aSuccess) {
+ client->mListener->ResourceReserved();
+ } else {
+ client->mListener->ResourceReserveFailed();
+ }
+ }
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaSystemResourceManager.h b/dom/media/systemservices/MediaSystemResourceManager.h
new file mode 100644
index 000000000..36869e202
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceManager.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(MediaSystemResourceManager_h_)
+#define MediaSystemResourceManager_h_
+
+#include <queue>
+
+#include "MediaSystemResourceTypes.h"
+#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/StaticPtr.h"
+#include "nsDataHashtable.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+namespace media {
+class MediaSystemResourceManagerChild;
+} // namespace media
+
+class MediaSystemResourceClient;
+class MediaSystemResourceReservationListener;
+class ReentrantMonitor;
+class TaskQueue;
+
+/**
+ * Manage media system resource allocation requests within a process.
+ */
+class MediaSystemResourceManager
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSystemResourceManager)
+
+ static MediaSystemResourceManager* Get();
+ static void Init();
+ static void Shutdown();
+
+ void OnIpcClosed();
+
+ void Register(MediaSystemResourceClient* aClient);
+ void Unregister(MediaSystemResourceClient* aClient);
+
+ bool SetListener(MediaSystemResourceClient* aClient,
+ MediaSystemResourceReservationListener* aListener);
+
+ void Acquire(MediaSystemResourceClient* aClient);
+ bool AcquireSyncNoWait(MediaSystemResourceClient* aClient);
+ void ReleaseResource(MediaSystemResourceClient* aClient);
+
+ void RecvResponse(uint32_t aId, bool aSuccess);
+
+private:
+ MediaSystemResourceManager();
+ virtual ~MediaSystemResourceManager();
+
+ void OpenIPC();
+ void CloseIPC();
+ bool IsIpcClosed();
+
+ void DoAcquire(uint32_t aId);
+
+ void DoRelease(uint32_t aId);
+
+ void HandleAcquireResult(uint32_t aId, bool aSuccess);
+
+ ReentrantMonitor mReentrantMonitor;
+
+ bool mShutDown;
+
+ media::MediaSystemResourceManagerChild* mChild;
+
+ nsDataHashtable<nsUint32HashKey, MediaSystemResourceClient*> mResourceClients;
+
+ static StaticRefPtr<MediaSystemResourceManager> sSingleton;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/systemservices/MediaSystemResourceManagerChild.cpp b/dom/media/systemservices/MediaSystemResourceManagerChild.cpp
new file mode 100644
index 000000000..284473821
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceManagerChild.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "MediaSystemResourceManager.h"
+
+#include "MediaSystemResourceManagerChild.h"
+
+namespace mozilla {
+namespace media {
+
+MediaSystemResourceManagerChild::MediaSystemResourceManagerChild()
+ : mDestroyed(false)
+{
+}
+
+MediaSystemResourceManagerChild::~MediaSystemResourceManagerChild()
+{
+}
+
+bool
+MediaSystemResourceManagerChild::RecvResponse(const uint32_t& aId,
+ const bool& aSuccess)
+{
+ if (mManager) {
+ mManager->RecvResponse(aId, aSuccess);
+ }
+ return true;
+}
+
+void
+MediaSystemResourceManagerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason)
+{
+ MOZ_ASSERT(!mDestroyed);
+ if (mManager) {
+ mManager->OnIpcClosed();
+ }
+ mDestroyed = true;
+}
+
+void
+MediaSystemResourceManagerChild::Destroy()
+{
+ if (mDestroyed) {
+ return;
+ }
+ SendRemoveResourceManager();
+ // WARNING: |this| is dead, hands off
+}
+
+} // namespace media
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaSystemResourceManagerChild.h b/dom/media/systemservices/MediaSystemResourceManagerChild.h
new file mode 100644
index 000000000..51d4de8d1
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceManagerChild.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#if !defined(MediaSystemResourceManagerChild_h_)
+#define MediaSystemResourceManagerChild_h_
+
+#include "mozilla/media/PMediaSystemResourceManagerChild.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class MediaSystemResourceManager;
+
+namespace ipc {
+class BackgroundChildImpl;
+} // namespace ipc
+
+namespace media {
+
+/**
+ * Handle MediaSystemResourceManager's IPC
+ */
+class MediaSystemResourceManagerChild final : public PMediaSystemResourceManagerChild
+{
+public:
+ struct ResourceListener {
+ /* The resource is reserved and can be granted.
+ * The client can allocate the requested resource.
+ */
+ virtual void resourceReserved() = 0;
+ /* The resource is not reserved any more.
+ * The client should release the resource as soon as possible if the
+ * resource is still being held.
+ */
+ virtual void resourceCanceled() = 0;
+ };
+
+ MediaSystemResourceManagerChild();
+ virtual ~MediaSystemResourceManagerChild();
+
+ void Destroy();
+
+ void SetManager(MediaSystemResourceManager* aManager)
+ {
+ mManager = aManager;
+ }
+
+protected:
+ bool RecvResponse(const uint32_t& aId,
+ const bool& aSuccess) override;
+
+private:
+ void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+ bool mDestroyed;
+ MediaSystemResourceManager* mManager;
+
+ friend class mozilla::ipc::BackgroundChildImpl;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/systemservices/MediaSystemResourceManagerParent.cpp b/dom/media/systemservices/MediaSystemResourceManagerParent.cpp
new file mode 100644
index 000000000..9e15c0c48
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceManagerParent.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "mozilla/Unused.h"
+
+#include "MediaSystemResourceManagerParent.h"
+
+namespace mozilla {
+namespace media {
+
+using namespace ipc;
+
+MediaSystemResourceManagerParent::MediaSystemResourceManagerParent()
+ : mDestroyed(false)
+{
+ mMediaSystemResourceService = MediaSystemResourceService::Get();
+}
+
+MediaSystemResourceManagerParent::~MediaSystemResourceManagerParent()
+{
+ MOZ_ASSERT(mDestroyed);
+}
+
+bool
+MediaSystemResourceManagerParent::RecvAcquire(const uint32_t& aId,
+ const MediaSystemResourceType& aResourceType,
+ const bool& aWillWait)
+{
+ MediaSystemResourceRequest* request = mResourceRequests.Get(aId);
+ MOZ_ASSERT(!request);
+ if (request) {
+ // Send fail response
+ mozilla::Unused << SendResponse(aId, false /* fail */);
+ return true;
+ }
+
+ request = new MediaSystemResourceRequest(aId, aResourceType);
+ mResourceRequests.Put(aId, request);
+ mMediaSystemResourceService->Acquire(this, aId, aResourceType, aWillWait);
+ return true;
+}
+
+bool
+MediaSystemResourceManagerParent::RecvRelease(const uint32_t& aId)
+{
+ MediaSystemResourceRequest* request = mResourceRequests.Get(aId);
+ if (!request) {
+ return true;
+ }
+
+ mMediaSystemResourceService->ReleaseResource(this, aId, request->mResourceType);
+ mResourceRequests.Remove(aId);
+ return true;
+}
+
+bool
+MediaSystemResourceManagerParent::RecvRemoveResourceManager()
+{
+ return PMediaSystemResourceManagerParent::Send__delete__(this);
+}
+
+void
+MediaSystemResourceManagerParent::ActorDestroy(ActorDestroyReason aReason)
+{
+ MOZ_ASSERT(!mDestroyed);
+
+ // Release all resource requests of the MediaSystemResourceManagerParent.
+ // Clears all remaining pointers to this object.
+ mMediaSystemResourceService->ReleaseResource(this);
+
+ mDestroyed = true;
+}
+
+} // namespace media
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaSystemResourceManagerParent.h b/dom/media/systemservices/MediaSystemResourceManagerParent.h
new file mode 100644
index 000000000..9080cb385
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceManagerParent.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#if !defined(MediaSystemResourceManagerParent_h_)
+#define MediaSystemResourceManagerParent_h_
+
+#include "MediaSystemResourceManager.h"
+#include "MediaSystemResourceService.h"
+#include "MediaSystemResourceTypes.h"
+#include "mozilla/media/PMediaSystemResourceManagerParent.h"
+
+namespace mozilla {
+namespace media {
+
+/**
+ * Handle MediaSystemResourceManager's IPC
+ */
+class MediaSystemResourceManagerParent final : public PMediaSystemResourceManagerParent
+{
+public:
+ MediaSystemResourceManagerParent();
+ virtual ~MediaSystemResourceManagerParent();
+
+protected:
+ bool RecvAcquire(const uint32_t& aId,
+ const MediaSystemResourceType& aResourceType,
+ const bool& aWillWait) override;
+
+ bool RecvRelease(const uint32_t& aId) override;
+
+ bool RecvRemoveResourceManager() override;
+
+private:
+ void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+ struct MediaSystemResourceRequest {
+ MediaSystemResourceRequest()
+ : mId(-1), mResourceType(MediaSystemResourceType::INVALID_RESOURCE) {}
+ MediaSystemResourceRequest(uint32_t aId, MediaSystemResourceType aResourceType)
+ : mId(aId), mResourceType(aResourceType) {}
+ int32_t mId;
+ MediaSystemResourceType mResourceType;
+ };
+
+ bool mDestroyed;
+
+ RefPtr<MediaSystemResourceService> mMediaSystemResourceService;
+
+ nsClassHashtable<nsUint32HashKey, MediaSystemResourceRequest> mResourceRequests;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/systemservices/MediaSystemResourceMessageUtils.h b/dom/media/systemservices/MediaSystemResourceMessageUtils.h
new file mode 100644
index 000000000..851fbb253
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceMessageUtils.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(MediaSystemResourceMessageUtils_h_)
+#define MediaSystemResourceMessageUtils_h_
+
+#include "ipc/IPCMessageUtils.h"
+#include "MediaSystemResourceTypes.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::MediaSystemResourceType>
+ : public ContiguousEnumSerializer<
+ mozilla::MediaSystemResourceType,
+ mozilla::MediaSystemResourceType::VIDEO_DECODER,
+ mozilla::MediaSystemResourceType::INVALID_RESOURCE>
+{};
+
+} // namespace IPC
+
+#endif
diff --git a/dom/media/systemservices/MediaSystemResourceService.cpp b/dom/media/systemservices/MediaSystemResourceService.cpp
new file mode 100644
index 000000000..0e5d6a50c
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceService.cpp
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#include "MediaSystemResourceManagerParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/Unused.h"
+
+#include "MediaSystemResourceService.h"
+
+using namespace mozilla::layers;
+
+namespace mozilla {
+
+/* static */ StaticRefPtr<MediaSystemResourceService> MediaSystemResourceService::sSingleton;
+
+/* static */ MediaSystemResourceService*
+MediaSystemResourceService::Get()
+{
+ if (sSingleton) {
+ return sSingleton;
+ }
+ Init();
+ return sSingleton;
+}
+
+/* static */ void
+MediaSystemResourceService::Init()
+{
+ if (!sSingleton) {
+ sSingleton = new MediaSystemResourceService();
+ }
+}
+
+/* static */ void
+MediaSystemResourceService::Shutdown()
+{
+ if (sSingleton) {
+ sSingleton->Destroy();
+ sSingleton = nullptr;
+ }
+}
+
+MediaSystemResourceService::MediaSystemResourceService()
+ : mDestroyed(false)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+#ifdef MOZ_WIDGET_GONK
+ // The maximum number of hardware resoureces available.
+ // XXX need to hange to a dynamic way.
+ enum
+ {
+ VIDEO_DECODER_COUNT = 1,
+ VIDEO_ENCODER_COUNT = 1
+ };
+
+ MediaSystemResource* resource;
+
+ resource = new MediaSystemResource(VIDEO_DECODER_COUNT);
+ mResources.Put(static_cast<uint32_t>(MediaSystemResourceType::VIDEO_DECODER), resource);
+
+ resource = new MediaSystemResource(VIDEO_ENCODER_COUNT);
+ mResources.Put(static_cast<uint32_t>(MediaSystemResourceType::VIDEO_ENCODER), resource);
+#endif
+}
+
+MediaSystemResourceService::~MediaSystemResourceService()
+{
+}
+
+void
+MediaSystemResourceService::Destroy()
+{
+ mDestroyed = true;
+}
+
+void
+MediaSystemResourceService::Acquire(media::MediaSystemResourceManagerParent* aParent,
+ uint32_t aId,
+ MediaSystemResourceType aResourceType,
+ bool aWillWait)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aParent);
+
+ if (mDestroyed) {
+ return;
+ }
+
+ MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
+
+ if (!resource ||
+ resource->mResourceCount == 0) {
+ // Resource does not exit
+ // Send fail response
+ mozilla::Unused << aParent->SendResponse(aId, false /* fail */);
+ return;
+ }
+
+ // Try to acquire a resource
+ if (resource->mAcquiredRequests.size() < resource->mResourceCount) {
+ // Resource is available
+ resource->mAcquiredRequests.push_back(
+ MediaSystemResourceRequest(aParent, aId));
+ // Send success response
+ mozilla::Unused << aParent->SendResponse(aId, true /* success */);
+ return;
+ } else if (!aWillWait) {
+ // Resource is not available and do not wait.
+ // Send fail response
+ mozilla::Unused << aParent->SendResponse(aId, false /* fail */);
+ return;
+ }
+ // Wait until acquire.
+ resource->mWaitingRequests.push_back(
+ MediaSystemResourceRequest(aParent, aId));
+}
+
+void
+MediaSystemResourceService::ReleaseResource(media::MediaSystemResourceManagerParent* aParent,
+ uint32_t aId,
+ MediaSystemResourceType aResourceType)
+{
+ MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+ MOZ_ASSERT(aParent);
+
+ if (mDestroyed) {
+ return;
+ }
+
+ MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
+
+ if (!resource ||
+ resource->mResourceCount == 0) {
+ // Resource does not exit
+ return;
+ }
+ RemoveRequest(aParent, aId, aResourceType);
+ UpdateRequests(aResourceType);
+}
+
+void
+MediaSystemResourceService::ReleaseResource(media::MediaSystemResourceManagerParent* aParent)
+{
+ MOZ_ASSERT(aParent);
+
+ if (mDestroyed) {
+ return;
+ }
+
+ for (auto iter = mResources.Iter(); !iter.Done(); iter.Next()) {
+ const uint32_t& key = iter.Key();
+ RemoveRequests(aParent, static_cast<MediaSystemResourceType>(key));
+ UpdateRequests(static_cast<MediaSystemResourceType>(key));
+ }
+}
+
+void
+MediaSystemResourceService::RemoveRequest(media::MediaSystemResourceManagerParent* aParent,
+ uint32_t aId,
+ MediaSystemResourceType aResourceType)
+{
+ MOZ_ASSERT(aParent);
+
+ MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
+ if (!resource) {
+ return;
+ }
+
+ std::deque<MediaSystemResourceRequest>::iterator it;
+ std::deque<MediaSystemResourceRequest>& acquiredRequests =
+ resource->mAcquiredRequests;
+ for (it = acquiredRequests.begin(); it != acquiredRequests.end(); it++) {
+ if (((*it).mParent == aParent) && ((*it).mId == aId)) {
+ acquiredRequests.erase(it);
+ return;
+ }
+ }
+
+ std::deque<MediaSystemResourceRequest>& waitingRequests =
+ resource->mWaitingRequests;
+ for (it = waitingRequests.begin(); it != waitingRequests.end(); it++) {
+ if (((*it).mParent == aParent) && ((*it).mId == aId)) {
+ waitingRequests.erase(it);
+ return;
+ }
+ }
+}
+
+void
+MediaSystemResourceService::RemoveRequests(media::MediaSystemResourceManagerParent* aParent,
+ MediaSystemResourceType aResourceType)
+{
+ MOZ_ASSERT(aParent);
+
+ MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
+
+ if (!resource ||
+ resource->mResourceCount == 0) {
+ // Resource does not exit
+ return;
+ }
+
+ std::deque<MediaSystemResourceRequest>::iterator it;
+ std::deque<MediaSystemResourceRequest>& acquiredRequests =
+ resource->mAcquiredRequests;
+ for (it = acquiredRequests.begin(); it != acquiredRequests.end();) {
+ if ((*it).mParent == aParent) {
+ it = acquiredRequests.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ std::deque<MediaSystemResourceRequest>& waitingRequests =
+ resource->mWaitingRequests;
+ for (it = waitingRequests.begin(); it != waitingRequests.end();) {
+ if ((*it).mParent == aParent) {
+ it = waitingRequests.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+void
+MediaSystemResourceService::UpdateRequests(MediaSystemResourceType aResourceType)
+{
+ MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
+
+ if (!resource ||
+ resource->mResourceCount == 0) {
+ // Resource does not exit
+ return;
+ }
+
+ std::deque<MediaSystemResourceRequest>& acquiredRequests =
+ resource->mAcquiredRequests;
+ std::deque<MediaSystemResourceRequest>& waitingRequests =
+ resource->mWaitingRequests;
+
+ while ((acquiredRequests.size() < resource->mResourceCount) &&
+ (waitingRequests.size() > 0)) {
+ MediaSystemResourceRequest& request = waitingRequests.front();
+ MOZ_ASSERT(request.mParent);
+ // Send response
+ mozilla::Unused << request.mParent->SendResponse(request.mId, true /* success */);
+ // Move request to mAcquiredRequests
+ acquiredRequests.push_back(waitingRequests.front());
+ waitingRequests.pop_front();
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaSystemResourceService.h b/dom/media/systemservices/MediaSystemResourceService.h
new file mode 100644
index 000000000..06e1b537c
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceService.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(MediaSystemResourceService_h_)
+#define MediaSystemResourceService_h_
+
+#include <deque>
+
+#include "MediaSystemResourceTypes.h"
+#include "mozilla/StaticPtr.h"
+#include "nsClassHashtable.h"
+
+namespace mozilla {
+
+namespace media {
+class MediaSystemResourceManagerParent;
+} // namespace media
+
+/**
+ * Manage media system resource allocation requests within system.
+ */
+class MediaSystemResourceService
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSystemResourceService)
+
+ static MediaSystemResourceService* Get();
+ static void Init();
+ static void Shutdown();
+
+ void Acquire(media::MediaSystemResourceManagerParent* aParent,
+ uint32_t aId,
+ MediaSystemResourceType aResourceType,
+ bool aWillWait);
+
+ void ReleaseResource(media::MediaSystemResourceManagerParent* aParent,
+ uint32_t aId,
+ MediaSystemResourceType aResourceType);
+
+ void ReleaseResource(media::MediaSystemResourceManagerParent* aParent);
+
+private:
+ MediaSystemResourceService();
+ ~MediaSystemResourceService();
+
+ struct MediaSystemResourceRequest {
+ MediaSystemResourceRequest()
+ : mParent(nullptr), mId(-1) {}
+ MediaSystemResourceRequest(media::MediaSystemResourceManagerParent* aParent, uint32_t aId)
+ : mParent(aParent), mId(aId) {}
+ media::MediaSystemResourceManagerParent* mParent;
+ uint32_t mId;
+ };
+
+ struct MediaSystemResource {
+ MediaSystemResource()
+ : mResourceCount(0) {}
+ explicit MediaSystemResource(uint32_t aResourceCount)
+ : mResourceCount(aResourceCount) {}
+
+ std::deque<MediaSystemResourceRequest> mWaitingRequests;
+ std::deque<MediaSystemResourceRequest> mAcquiredRequests;
+ uint32_t mResourceCount;
+ };
+
+ void Destroy();
+
+ void RemoveRequest(media::MediaSystemResourceManagerParent* aParent,
+ uint32_t aId,
+ MediaSystemResourceType aResourceType);
+
+ void RemoveRequests(media::MediaSystemResourceManagerParent* aParent,
+ MediaSystemResourceType aResourceType);
+
+ void UpdateRequests(MediaSystemResourceType aResourceType);
+
+ bool mDestroyed;
+
+ nsClassHashtable<nsUint32HashKey, MediaSystemResource> mResources;
+
+ static StaticRefPtr<MediaSystemResourceService> sSingleton;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/systemservices/MediaSystemResourceTypes.h b/dom/media/systemservices/MediaSystemResourceTypes.h
new file mode 100644
index 000000000..3257d89d2
--- /dev/null
+++ b/dom/media/systemservices/MediaSystemResourceTypes.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#if !defined(MediaSystemResourceTypes_h_)
+#define MediaSystemResourceTypes_h_
+
+namespace mozilla {
+
+enum class MediaSystemResourceType : uint32_t {
+ VIDEO_DECODER = 0,
+ AUDIO_DECODER, // Not supported currently.
+ VIDEO_ENCODER,
+ AUDIO_ENCODER, // Not supported currently.
+ CAMERA, // Not supported currently.
+ INVALID_RESOURCE,
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/systemservices/MediaTaskUtils.h b/dom/media/systemservices/MediaTaskUtils.h
new file mode 100644
index 000000000..c9b64fd2a
--- /dev/null
+++ b/dom/media/systemservices/MediaTaskUtils.h
@@ -0,0 +1,50 @@
+/* -*- 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_MediaTaskUtils_h
+#define mozilla_MediaTaskUtils_h
+
+#include "nsThreadUtils.h"
+
+// The main reason this file is separate from MediaUtils.h
+#include "base/task.h"
+
+namespace mozilla {
+namespace media {
+
+/* media::NewTaskFrom() - Create a Task from a lambda.
+ *
+ * Similar to media::NewRunnableFrom() - Create an nsRunnable from a lambda.
+ */
+
+template<typename OnRunType>
+class LambdaTask : public Runnable
+{
+public:
+ explicit LambdaTask(OnRunType&& aOnRun) : mOnRun(Move(aOnRun)) {}
+private:
+ NS_IMETHOD
+ Run() override
+ {
+ mOnRun();
+ return NS_OK;
+ }
+ OnRunType mOnRun;
+};
+
+template<typename OnRunType>
+already_AddRefed<LambdaTask<OnRunType>>
+NewTaskFrom(OnRunType&& aOnRun)
+{
+ typedef LambdaTask<OnRunType> LambdaType;
+ RefPtr<LambdaType> lambda = new LambdaType(Forward<OnRunType>(aOnRun));
+ return lambda.forget();
+}
+
+} // namespace media
+} // namespace mozilla
+
+#endif // mozilla_MediaTaskUtils_h
diff --git a/dom/media/systemservices/MediaUtils.cpp b/dom/media/systemservices/MediaUtils.cpp
new file mode 100644
index 000000000..a77e3404a
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.cpp
@@ -0,0 +1,15 @@
+/* -*- 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/. */
+
+#include "MediaUtils.h"
+
+namespace mozilla {
+namespace media {
+
+NS_IMPL_ISUPPORTS(ShutdownBlocker, nsIAsyncShutdownBlocker)
+
+} // namespace media
+} // namespace mozilla
diff --git a/dom/media/systemservices/MediaUtils.h b/dom/media/systemservices/MediaUtils.h
new file mode 100644
index 000000000..18f7d3e41
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.h
@@ -0,0 +1,373 @@
+/* -*- 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_MediaUtils_h
+#define mozilla_MediaUtils_h
+
+#include "nsThreadUtils.h"
+#include "nsIAsyncShutdown.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace media {
+
+/*
+ * media::Pledge - A promise-like pattern for c++ that takes lambda functions.
+ *
+ * Asynchronous APIs that proxy to another thread or to the chrome process and
+ * back may find it useful to return a pledge to callers who then use
+ * pledge.Then(func) to specify a lambda function to be invoked with the result
+ * later back on this same thread.
+ *
+ * Callers will enjoy that lambdas allow "capturing" of local variables, much
+ * like closures in JavaScript (safely by-copy by default).
+ *
+ * Callers will also enjoy that they do not need to be thread-safe (their code
+ * runs on the same thread after all).
+ *
+ * Advantageously, pledges are non-threadsafe by design (because locking and
+ * event queues are redundant). This means none of the lambdas you pass in,
+ * or variables you lambda-capture into them, need be threasafe or support
+ * threadsafe refcounting. After all, they'll run later on the same thread.
+ *
+ * RefPtr<media::Pledge<Foo>> p = GetFooAsynchronously(); // returns a pledge
+ * p->Then([](const Foo& foo) {
+ * // use foo here (same thread. Need not be thread-safe!)
+ * });
+ *
+ * See media::CoatCheck below for an example of GetFooAsynchronously().
+ */
+
+class PledgeBase
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(PledgeBase);
+protected:
+ virtual ~PledgeBase() {};
+};
+
+template<typename ValueType, typename ErrorType = nsresult>
+class Pledge : public PledgeBase
+{
+ // TODO: Remove workaround once mozilla allows std::function from <functional>
+ // wo/std::function support, do template + virtual trick to accept lambdas
+ class FunctorsBase
+ {
+ public:
+ FunctorsBase() {}
+ virtual void Succeed(ValueType& result) = 0;
+ virtual void Fail(ErrorType& error) = 0;
+ virtual ~FunctorsBase() {};
+ };
+
+public:
+ explicit Pledge() : mDone(false), mRejected(false) {}
+ Pledge(const Pledge& aOther) = delete;
+ Pledge& operator = (const Pledge&) = delete;
+
+ template<typename OnSuccessType>
+ void Then(OnSuccessType&& aOnSuccess)
+ {
+ Then(Forward<OnSuccessType>(aOnSuccess), [](ErrorType&){});
+ }
+
+ template<typename OnSuccessType, typename OnFailureType>
+ void Then(OnSuccessType&& aOnSuccess, OnFailureType&& aOnFailure)
+ {
+ class Functors : public FunctorsBase
+ {
+ public:
+ Functors(OnSuccessType&& aOnSuccessRef, OnFailureType&& aOnFailureRef)
+ : mOnSuccess(Move(aOnSuccessRef)), mOnFailure(Move(aOnFailureRef)) {}
+
+ void Succeed(ValueType& result)
+ {
+ mOnSuccess(result);
+ }
+ void Fail(ErrorType& error)
+ {
+ mOnFailure(error);
+ };
+
+ OnSuccessType mOnSuccess;
+ OnFailureType mOnFailure;
+ };
+ mFunctors = MakeUnique<Functors>(Forward<OnSuccessType>(aOnSuccess),
+ Forward<OnFailureType>(aOnFailure));
+ if (mDone) {
+ if (!mRejected) {
+ mFunctors->Succeed(mValue);
+ } else {
+ mFunctors->Fail(mError);
+ }
+ }
+ }
+
+ void Resolve(const ValueType& aValue)
+ {
+ mValue = aValue;
+ Resolve();
+ }
+
+ void Reject(ErrorType rv)
+ {
+ if (!mDone) {
+ mDone = mRejected = true;
+ mError = rv;
+ if (mFunctors) {
+ mFunctors->Fail(mError);
+ }
+ }
+ }
+
+protected:
+ void Resolve()
+ {
+ if (!mDone) {
+ mDone = true;
+ MOZ_ASSERT(!mRejected);
+ if (mFunctors) {
+ mFunctors->Succeed(mValue);
+ }
+ }
+ }
+
+ ValueType mValue;
+private:
+ ~Pledge() {};
+ bool mDone;
+ bool mRejected;
+ ErrorType mError;
+ UniquePtr<FunctorsBase> mFunctors;
+};
+
+/* media::NewRunnableFrom() - Create a Runnable from a lambda.
+ *
+ * Passing variables (closures) to an async function is clunky with Runnable:
+ *
+ * void Foo()
+ * {
+ * class FooRunnable : public Runnable
+ * {
+ * public:
+ * FooRunnable(const Bar &aBar) : mBar(aBar) {}
+ * NS_IMETHOD Run() override
+ * {
+ * // Use mBar
+ * }
+ * private:
+ * RefPtr<Bar> mBar;
+ * };
+ *
+ * RefPtr<Bar> bar = new Bar();
+ * NS_DispatchToMainThread(new FooRunnable(bar);
+ * }
+ *
+ * It's worse with more variables. Lambdas have a leg up with variable capture:
+ *
+ * void Foo()
+ * {
+ * RefPtr<Bar> bar = new Bar();
+ * NS_DispatchToMainThread(media::NewRunnableFrom([bar]() mutable {
+ * // use bar
+ * });
+ * }
+ *
+ * Capture is by-copy by default, so the nsRefPtr 'bar' is safely copied for
+ * access on the other thread (threadsafe refcounting in bar is assumed).
+ *
+ * The 'mutable' keyword is only needed for non-const access to bar.
+ */
+
+template<typename OnRunType>
+class LambdaRunnable : public Runnable
+{
+public:
+ explicit LambdaRunnable(OnRunType&& aOnRun) : mOnRun(Move(aOnRun)) {}
+private:
+ NS_IMETHODIMP
+ Run() override
+ {
+ return mOnRun();
+ }
+ OnRunType mOnRun;
+};
+
+template<typename OnRunType>
+already_AddRefed<LambdaRunnable<OnRunType>>
+NewRunnableFrom(OnRunType&& aOnRun)
+{
+ typedef LambdaRunnable<OnRunType> LambdaType;
+ RefPtr<LambdaType> lambda = new LambdaType(Forward<OnRunType>(aOnRun));
+ return lambda.forget();
+}
+
+/* media::CoatCheck - There and back again. Park an object in exchange for an id.
+ *
+ * A common problem with calling asynchronous functions that do work on other
+ * threads or processes is how to pass in a heap object for use once the
+ * function completes, without requiring that object to have threadsafe
+ * refcounting, contain mutexes, be marshaled, or leak if things fail
+ * (or worse, intermittent use-after-free because of lifetime issues).
+ *
+ * One solution is to set up a coat-check on the caller side, park your object
+ * in exchange for an id, and send the id. Common in IPC, but equally useful
+ * for same-process thread-hops, because by never leaving the thread there's
+ * no need for objects to be threadsafe or use threadsafe refcounting. E.g.
+ *
+ * class FooDoer
+ * {
+ * CoatCheck<Foo> mOutstandingFoos;
+ *
+ * public:
+ * void DoFoo()
+ * {
+ * RefPtr<Foo> foo = new Foo();
+ * uint32_t requestId = mOutstandingFoos.Append(*foo);
+ * sChild->SendFoo(requestId);
+ * }
+ *
+ * void RecvFooResponse(uint32_t requestId)
+ * {
+ * RefPtr<Foo> foo = mOutstandingFoos.Remove(requestId);
+ * if (foo) {
+ * // use foo
+ * }
+ * }
+ * };
+ *
+ * If you read media::Pledge earlier, here's how this is useful for pledges:
+ *
+ * class FooGetter
+ * {
+ * CoatCheck<Pledge<Foo>> mOutstandingPledges;
+ *
+ * public:
+ * already_addRefed<Pledge<Foo>> GetFooAsynchronously()
+ * {
+ * RefPtr<Pledge<Foo>> p = new Pledge<Foo>();
+ * uint32_t requestId = mOutstandingPledges.Append(*p);
+ * sChild->SendFoo(requestId);
+ * return p.forget();
+ * }
+ *
+ * void RecvFooResponse(uint32_t requestId, const Foo& fooResult)
+ * {
+ * RefPtr<Foo> p = mOutstandingPledges.Remove(requestId);
+ * if (p) {
+ * p->Resolve(fooResult);
+ * }
+ * }
+ * };
+ *
+ * This helper is currently optimized for very small sets (i.e. not optimized).
+ * It is also not thread-safe as the whole point is to stay on the same thread.
+ */
+
+template<class T>
+class CoatCheck
+{
+public:
+ typedef std::pair<uint32_t, RefPtr<T>> Element;
+
+ uint32_t Append(T& t)
+ {
+ uint32_t id = GetNextId();
+ mElements.AppendElement(Element(id, RefPtr<T>(&t)));
+ return id;
+ }
+
+ already_AddRefed<T> Remove(uint32_t aId)
+ {
+ for (auto& element : mElements) {
+ if (element.first == aId) {
+ RefPtr<T> ref;
+ ref.swap(element.second);
+ mElements.RemoveElement(element);
+ return ref.forget();
+ }
+ }
+ MOZ_ASSERT_UNREACHABLE("Received id with no matching parked object!");
+ return nullptr;
+ }
+
+private:
+ static uint32_t GetNextId()
+ {
+ static uint32_t counter = 0;
+ return ++counter;
+ };
+ AutoTArray<Element, 3> mElements;
+};
+
+/* media::Refcountable - Add threadsafe ref-counting to something that isn't.
+ *
+ * Often, reference counting is the most practical way to share an object with
+ * another thread without imposing lifetime restrictions, even if there's
+ * otherwise no concurrent access happening on the object. For instance, an
+ * algorithm on another thread may find it more expedient to modify a passed-in
+ * object, rather than pass expensive copies back and forth.
+ *
+ * Lists in particular often aren't ref-countable, yet are expensive to copy,
+ * e.g. nsTArray<RefPtr<Foo>>. Refcountable can be used to make such objects
+ * (or owning smart-pointers to such objects) refcountable.
+ *
+ * Technical limitation: A template specialization is needed for types that take
+ * a constructor. Please add below (UniquePtr covers a lot of ground though).
+ */
+
+template<typename T>
+class Refcountable : public T
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
+private:
+ ~Refcountable<T>() {}
+};
+
+template<typename T>
+class Refcountable<UniquePtr<T>> : public UniquePtr<T>
+{
+public:
+ explicit Refcountable<UniquePtr<T>>(T* aPtr) : UniquePtr<T>(aPtr) {}
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
+private:
+ ~Refcountable<UniquePtr<T>>() {}
+};
+
+/* media::ShutdownBlocker - Async shutdown helper.
+ */
+
+class ShutdownBlocker : public nsIAsyncShutdownBlocker
+{
+public:
+ ShutdownBlocker(const nsString& aName) : mName(aName) {}
+
+ NS_IMETHOD
+ BlockShutdown(nsIAsyncShutdownClient* aProfileBeforeChange) override = 0;
+
+ NS_IMETHOD GetName(nsAString& aName) override
+ {
+ aName = mName;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetState(nsIPropertyBag**) override
+ {
+ return NS_OK;
+ }
+
+ NS_DECL_ISUPPORTS
+protected:
+ virtual ~ShutdownBlocker() {}
+private:
+ const nsString mName;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif // mozilla_MediaUtils_h
diff --git a/dom/media/systemservices/OSXRunLoopSingleton.cpp b/dom/media/systemservices/OSXRunLoopSingleton.cpp
new file mode 100644
index 000000000..6211d5c12
--- /dev/null
+++ b/dom/media/systemservices/OSXRunLoopSingleton.cpp
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#include "OSXRunLoopSingleton.h"
+#include <mozilla/StaticMutex.h>
+
+#include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/AudioHardware.h>
+#include <CoreAudio/HostTime.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+static bool gRunLoopSet = false;
+static mozilla::StaticMutex gMutex;
+
+void mozilla_set_coreaudio_notification_runloop_if_needed()
+{
+ mozilla::StaticMutexAutoLock lock(gMutex);
+ if (gRunLoopSet) {
+ return;
+ }
+
+ /* This is needed so that AudioUnit listeners get called on this thread, and
+ * not the main thread. If we don't do that, they are not called, or a crash
+ * occur, depending on the OSX version. */
+ AudioObjectPropertyAddress runloop_address = {
+ kAudioHardwarePropertyRunLoop,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ CFRunLoopRef run_loop = nullptr;
+
+ OSStatus r;
+ r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
+ &runloop_address,
+ 0, NULL, sizeof(CFRunLoopRef), &run_loop);
+ if (r != noErr) {
+ NS_WARNING("Could not make global CoreAudio notifications use their own thread.");
+ }
+
+ gRunLoopSet = true;
+}
diff --git a/dom/media/systemservices/OSXRunLoopSingleton.h b/dom/media/systemservices/OSXRunLoopSingleton.h
new file mode 100644
index 000000000..d06365e14
--- /dev/null
+++ b/dom/media/systemservices/OSXRunLoopSingleton.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 2; 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 OSXRUNLOOPSINGLETON_H_
+#define OSXRUNLOOPSINGLETON_H_
+
+#include <mozilla/Types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* This function tells CoreAudio to use its own thread for device change
+ * notifications, and can be called from any thread without external
+ * synchronization. */
+void MOZ_EXPORT
+mozilla_set_coreaudio_notification_runloop_if_needed();
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // OSXRUNLOOPSINGLETON_H_
diff --git a/dom/media/systemservices/OpenSLESProvider.cpp b/dom/media/systemservices/OpenSLESProvider.cpp
new file mode 100644
index 000000000..c7348afa0
--- /dev/null
+++ b/dom/media/systemservices/OpenSLESProvider.cpp
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 50; 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/. */
+
+#include "OpenSLESProvider.h"
+#include "mozilla/Logging.h"
+#include "nsDebug.h"
+
+#include <dlfcn.h>
+#include <SLES/OpenSLES_Android.h>
+#include <SLES/OpenSLES_AndroidConfiguration.h>
+
+// MOZ_LOG=OpenSLESProvider:5
+#undef LOG
+#undef LOG_ENABLED
+mozilla::LazyLogModule gOpenSLESProviderLog("OpenSLESProvider");
+#define LOG(args) MOZ_LOG(gOpenSLESProviderLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gOpenSLESProviderLog, mozilla::LogLevel::Debug)
+
+namespace mozilla {
+
+OpenSLESProvider::OpenSLESProvider()
+ : mLock("OpenSLESProvider.mLock"),
+ mSLEngine(nullptr),
+ mSLEngineUsers(0),
+ mIsRealized(false),
+ mOpenSLESLib(nullptr)
+{
+ LOG(("OpenSLESProvider being initialized"));
+}
+
+OpenSLESProvider::~OpenSLESProvider()
+{
+ if (mOpenSLESLib) {
+ LOG(("OpenSLES Engine was not properly Destroyed"));
+ (void)dlclose(mOpenSLESLib);
+ }
+}
+
+/* static */
+OpenSLESProvider& OpenSLESProvider::getInstance()
+{
+ // This doesn't need a Mutex in C++11 or GCC 4.3+, see N2660 and
+ // https://gcc.gnu.org/projects/cxx0x.html
+ static OpenSLESProvider instance;
+ return instance;
+}
+
+/* static */
+SLresult OpenSLESProvider::Get(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions)
+{
+ OpenSLESProvider& provider = OpenSLESProvider::getInstance();
+ return provider.GetEngine(aObjectm, aOptionCount, aOptions);
+}
+
+SLresult OpenSLESProvider::GetEngine(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions)
+{
+ MutexAutoLock lock(mLock);
+ LOG(("Getting OpenSLES engine"));
+ // Bug 1042051: Validate options are the same
+ if (mSLEngine != nullptr) {
+ *aObjectm = mSLEngine;
+ mSLEngineUsers++;
+ LOG(("Returning existing engine, %d users", mSLEngineUsers));
+ return SL_RESULT_SUCCESS;
+ } else {
+ int res = ConstructEngine(aObjectm, aOptionCount, aOptions);
+ if (res == SL_RESULT_SUCCESS) {
+ // Bug 1042051: Store engine options
+ mSLEngine = *aObjectm;
+ mSLEngineUsers++;
+ LOG(("Returning new engine"));
+ } else {
+ LOG(("Error getting engine: %d", res));
+ }
+ return res;
+ }
+}
+
+SLresult OpenSLESProvider::ConstructEngine(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions)
+{
+ mLock.AssertCurrentThreadOwns();
+
+ if (!mOpenSLESLib) {
+ mOpenSLESLib = dlopen("libOpenSLES.so", RTLD_LAZY);
+ if (!mOpenSLESLib) {
+ LOG(("Failed to dlopen OpenSLES library"));
+ return SL_RESULT_MEMORY_FAILURE;
+ }
+ }
+
+ typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
+ SLuint32,
+ const SLEngineOption *,
+ SLuint32,
+ const SLInterfaceID *,
+ const SLboolean *);
+
+ slCreateEngine_t f_slCreateEngine =
+ (slCreateEngine_t)dlsym(mOpenSLESLib, "slCreateEngine");
+ int result = f_slCreateEngine(aObjectm, aOptionCount, aOptions, 0, NULL, NULL);
+ return result;
+}
+
+/* static */
+void OpenSLESProvider::Destroy(SLObjectItf * aObjectm)
+{
+ OpenSLESProvider& provider = OpenSLESProvider::getInstance();
+ provider.DestroyEngine(aObjectm);
+}
+
+void OpenSLESProvider::DestroyEngine(SLObjectItf * aObjectm)
+{
+ MutexAutoLock lock(mLock);
+ NS_ASSERTION(mOpenSLESLib, "OpenSLES destroy called but library is not open");
+
+ mSLEngineUsers--;
+ LOG(("Freeing engine, %d users left", mSLEngineUsers));
+ if (mSLEngineUsers) {
+ return;
+ }
+
+ (*(*aObjectm))->Destroy(*aObjectm);
+ // This assumes SLObjectItf is a pointer, but given the previous line,
+ // that's a given.
+ *aObjectm = nullptr;
+
+ (void)dlclose(mOpenSLESLib);
+ mOpenSLESLib = nullptr;
+ mIsRealized = false;
+}
+
+/* static */
+SLresult OpenSLESProvider::Realize(SLObjectItf aObjectm)
+{
+ OpenSLESProvider& provider = OpenSLESProvider::getInstance();
+ return provider.RealizeEngine(aObjectm);
+}
+
+SLresult OpenSLESProvider::RealizeEngine(SLObjectItf aObjectm)
+{
+ MutexAutoLock lock(mLock);
+ NS_ASSERTION(mOpenSLESLib, "OpenSLES realize called but library is not open");
+ NS_ASSERTION(aObjectm != nullptr, "OpenSLES realize engine with empty ObjectItf");
+
+ if (mIsRealized) {
+ LOG(("Not realizing already realized engine"));
+ return SL_RESULT_SUCCESS;
+ } else {
+ SLresult res = (*aObjectm)->Realize(aObjectm, SL_BOOLEAN_FALSE);
+ if (res != SL_RESULT_SUCCESS) {
+ LOG(("Error realizing OpenSLES engine: %d", res));
+ } else {
+ LOG(("Realized OpenSLES engine"));
+ mIsRealized = true;
+ }
+ return res;
+ }
+}
+
+} // namespace mozilla
+
+extern "C" {
+SLresult mozilla_get_sles_engine(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions)
+{
+ return mozilla::OpenSLESProvider::Get(aObjectm, aOptionCount, aOptions);
+}
+
+void mozilla_destroy_sles_engine(SLObjectItf * aObjectm)
+{
+ mozilla::OpenSLESProvider::Destroy(aObjectm);
+}
+
+SLresult mozilla_realize_sles_engine(SLObjectItf aObjectm)
+{
+ return mozilla::OpenSLESProvider::Realize(aObjectm);
+}
+
+}
+
diff --git a/dom/media/systemservices/OpenSLESProvider.h b/dom/media/systemservices/OpenSLESProvider.h
new file mode 100644
index 000000000..6253e9519
--- /dev/null
+++ b/dom/media/systemservices/OpenSLESProvider.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 _OPENSLESPROVIDER_H_
+#define _OPENSLESPROVIDER_H_
+
+#include <SLES/OpenSLES.h>
+#include <mozilla/Types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern MOZ_EXPORT
+SLresult mozilla_get_sles_engine(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions);
+extern MOZ_EXPORT
+void mozilla_destroy_sles_engine(SLObjectItf * aObjectm);
+/* Realize is always in synchronous mode. */
+extern MOZ_EXPORT
+SLresult mozilla_realize_sles_engine(SLObjectItf aObjectm);
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+#include "mozilla/Mutex.h"
+
+extern mozilla::LazyLogModule gOpenSLESProviderLog;
+
+namespace mozilla {
+
+class OpenSLESProvider {
+public:
+ static SLresult Get(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions);
+ static void Destroy(SLObjectItf * aObjectm);
+ static SLresult Realize(SLObjectItf aObjectm);
+private:
+ OpenSLESProvider();
+ ~OpenSLESProvider();
+ OpenSLESProvider(OpenSLESProvider const&); // NO IMPLEMENTATION
+ void operator=(OpenSLESProvider const&); // NO IMPLEMENTATION
+ static OpenSLESProvider& getInstance();
+ SLresult GetEngine(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions);
+ SLresult ConstructEngine(SLObjectItf * aObjectm,
+ SLuint32 aOptionCount,
+ const SLEngineOption *aOptions);
+ SLresult RealizeEngine(SLObjectItf aObjectm);
+ void DestroyEngine(SLObjectItf * aObjectm);
+
+ // Protect all our internal variables
+ mozilla::Mutex mLock;
+ SLObjectItf mSLEngine;
+ int mSLEngineUsers;
+ bool mIsRealized;
+ void *mOpenSLESLib;
+};
+
+} //namespace
+#endif // cplusplus
+
+#endif /* _OPENSLESPROVIDER_H_ */
diff --git a/dom/media/systemservices/PCameras.ipdl b/dom/media/systemservices/PCameras.ipdl
new file mode 100644
index 000000000..b9fa58329
--- /dev/null
+++ b/dom/media/systemservices/PCameras.ipdl
@@ -0,0 +1,65 @@
+/* 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/. */
+
+include protocol PContent;
+include protocol PBackground;
+
+using mozilla::camera::CaptureEngine from "mozilla/media/CamerasTypes.h";
+
+namespace mozilla {
+namespace camera {
+
+struct CaptureCapability
+{
+ int width;
+ int height;
+ int maxFPS;
+ int expectedCaptureDelay;
+ int rawType;
+ int codecType;
+ bool interlaced;
+};
+
+async protocol PCameras
+{
+ manager PBackground;
+
+child:
+ async FrameSizeChange(CaptureEngine capEngine, int cap_id, int w, int h);
+ // transfers ownership of |buffer| from parent to child
+ async DeliverFrame(CaptureEngine capEngine, int cap_id,
+ Shmem buffer, size_t size, uint32_t time_stamp,
+ int64_t ntp_time, int64_t render_time);
+ async DeviceChange();
+ async ReplyNumberOfCaptureDevices(int numdev);
+ async ReplyNumberOfCapabilities(int numdev);
+ async ReplyAllocateCaptureDevice(int numdev);
+ async ReplyGetCaptureCapability(CaptureCapability cap);
+ async ReplyGetCaptureDevice(nsCString device_name, nsCString device_id, bool scary);
+ async ReplyFailure();
+ async ReplySuccess();
+ async __delete__();
+
+parent:
+ async NumberOfCaptureDevices(CaptureEngine engine);
+ async NumberOfCapabilities(CaptureEngine engine, nsCString deviceUniqueIdUTF8);
+
+ async GetCaptureCapability(CaptureEngine engine, nsCString unique_idUTF8, int capability_number);
+ async GetCaptureDevice(CaptureEngine engine, int num);
+
+ async AllocateCaptureDevice(CaptureEngine engine, nsCString unique_idUTF8, nsCString origin);
+ async ReleaseCaptureDevice(CaptureEngine engine, int numdev);
+ async StartCapture(CaptureEngine engine, int numdev, CaptureCapability capability);
+ async StopCapture(CaptureEngine engine, int numdev);
+ // transfers frame back
+ async ReleaseFrame(Shmem s);
+
+ // Ask parent to delete us
+ async AllDone();
+ // setup camera engine
+ async EnsureInitialized(CaptureEngine engine);
+};
+
+} // namespace camera
+} // namespace mozilla
diff --git a/dom/media/systemservices/PMedia.ipdl b/dom/media/systemservices/PMedia.ipdl
new file mode 100644
index 000000000..c060f030e
--- /dev/null
+++ b/dom/media/systemservices/PMedia.ipdl
@@ -0,0 +1,51 @@
+/* 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/. */
+
+include protocol PContent;
+
+namespace mozilla {
+namespace media {
+
+protocol PMedia
+{
+ manager PContent;
+
+parent:
+ /**
+ * Requests a potentially persistent unique secret key for each origin.
+ * Has no expiry, but is cleared by age along with cookies.
+ * This is needed by mediaDevices.enumerateDevices() to produce persistent
+ * deviceIds that wont work cross-origin.
+ *
+ * If aPrivateBrowsing is false, a key for this origin is returned from a
+ * primary pool of temporal in-memory keys and persistent keys read from disk.
+ * If no key exists, a temporal one is created.
+ * If aPersist is true and key is temporal, the key is promoted to persistent.
+ * Once persistent, a key cannot become temporal again.
+ *
+ * If aPrivateBrowsing is true, a different key for this origin is returned
+ * from a secondary pool that is never persisted to disk, and aPersist is
+ * ignored.
+ */
+ async GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing,
+ bool aPersist);
+
+ /**
+ * Clear per-orgin list of persistent deviceIds stored for enumerateDevices
+ * Fire and forget.
+ *
+ * aSinceTime - milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all
+ *
+ * aOnlyPrivateBrowsing - if true then only purge the separate in-memory
+ * per-origin list used in Private Browsing.
+ */
+ async SanitizeOriginKeys(uint64_t aSinceWhen, bool aOnlyPrivateBrowsing);
+
+child:
+ async GetOriginKeyResponse(uint32_t aRequestId, nsCString key);
+ async __delete__();
+};
+
+} // namespace media
+} // namespace mozilla
diff --git a/dom/media/systemservices/PMediaSystemResourceManager.ipdl b/dom/media/systemservices/PMediaSystemResourceManager.ipdl
new file mode 100644
index 000000000..8ba546765
--- /dev/null
+++ b/dom/media/systemservices/PMediaSystemResourceManager.ipdl
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+include protocol PImageBridge;
+include "mozilla/media/MediaSystemResourceMessageUtils.h";
+
+using mozilla::MediaSystemResourceType from "mozilla/media/MediaSystemResourceTypes.h";
+
+namespace mozilla {
+namespace media {
+
+/*
+ * The PMediaSystemResourceManager is a sub-protocol in PImageBridge
+ */
+sync protocol PMediaSystemResourceManager
+{
+ manager PImageBridge;
+
+child:
+ async Response(uint32_t aId, bool aSuccess);
+ async __delete__();
+
+parent:
+ async Acquire(uint32_t aId, MediaSystemResourceType aResourceType, bool aWillWait);
+ async Release(uint32_t aId);
+
+ /**
+ * Asynchronously tell the parent side to remove the PMediaSystemResourceManager.
+ */
+ async RemoveResourceManager();
+};
+
+} // namespace media
+} // namespace mozilla
+
diff --git a/dom/media/systemservices/ShmemPool.cpp b/dom/media/systemservices/ShmemPool.cpp
new file mode 100644
index 000000000..334a94e35
--- /dev/null
+++ b/dom/media/systemservices/ShmemPool.cpp
@@ -0,0 +1,82 @@
+/* -*- 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/. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ShmemPool.h"
+#include "mozilla/Move.h"
+
+namespace mozilla {
+
+ShmemPool::ShmemPool(size_t aPoolSize)
+ : mMutex("mozilla::ShmemPool"),
+ mPoolFree(aPoolSize)
+#ifdef DEBUG
+ ,mMaxPoolUse(0)
+#endif
+{
+ mShmemPool.SetLength(aPoolSize);
+}
+
+mozilla::ShmemBuffer ShmemPool::GetIfAvailable(size_t aSize)
+{
+ MutexAutoLock lock(mMutex);
+
+ // Pool is empty, don't block caller.
+ if (mPoolFree == 0) {
+ // This isn't initialized, so will be understood as an error.
+ return ShmemBuffer();
+ }
+
+ ShmemBuffer& res = mShmemPool[mPoolFree - 1];
+
+ if (!res.mInitialized) {
+ LOG(("No free preallocated Shmem"));
+ return ShmemBuffer();
+ }
+
+ MOZ_ASSERT(res.mShmem.IsWritable(), "Pool in Shmem is not writable?");
+
+ if (res.mShmem.Size<char>() < aSize) {
+ LOG(("Free Shmem but not of the right size"));
+ return ShmemBuffer();
+ }
+
+ mPoolFree--;
+#ifdef DEBUG
+ size_t poolUse = mShmemPool.Length() - mPoolFree;
+ if (poolUse > mMaxPoolUse) {
+ mMaxPoolUse = poolUse;
+ LOG(("Maximum ShmemPool use increased: %d buffers", mMaxPoolUse));
+ }
+#endif
+ return Move(res);
+}
+
+void ShmemPool::Put(ShmemBuffer&& aShmem)
+{
+ MutexAutoLock lock(mMutex);
+ MOZ_ASSERT(mPoolFree < mShmemPool.Length());
+ mShmemPool[mPoolFree] = Move(aShmem);
+ mPoolFree++;
+#ifdef DEBUG
+ size_t poolUse = mShmemPool.Length() - mPoolFree;
+ if (poolUse > 0) {
+ LOG(("ShmemPool usage reduced to %d buffers", poolUse));
+ }
+#endif
+}
+
+ShmemPool::~ShmemPool()
+{
+#ifdef DEBUG
+ for (size_t i = 0; i < mShmemPool.Length(); i++) {
+ MOZ_ASSERT(!mShmemPool[i].Valid());
+ }
+#endif
+}
+
+} // namespace mozilla
diff --git a/dom/media/systemservices/ShmemPool.h b/dom/media/systemservices/ShmemPool.h
new file mode 100644
index 000000000..95901ffa0
--- /dev/null
+++ b/dom/media/systemservices/ShmemPool.h
@@ -0,0 +1,153 @@
+/* -*- 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_ShmemPool_h
+#define mozilla_ShmemPool_h
+
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/Mutex.h"
+
+#undef LOG
+#undef LOG_ENABLED
+extern mozilla::LazyLogModule gCamerasParentLog;
+#define LOG(args) MOZ_LOG(gCamerasParentLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() MOZ_LOG_TEST(gCamerasParentLog, mozilla::LogLevel::Debug)
+
+namespace mozilla {
+
+class ShmemPool;
+
+class ShmemBuffer {
+public:
+ ShmemBuffer() : mInitialized(false) {}
+ explicit ShmemBuffer(mozilla::ipc::Shmem aShmem) {
+ mInitialized = true;
+ mShmem = aShmem;
+ }
+
+ ShmemBuffer(ShmemBuffer&& rhs) {
+ mInitialized = rhs.mInitialized;
+ mShmem = Move(rhs.mShmem);
+ }
+
+ ShmemBuffer& operator=(ShmemBuffer&& rhs) {
+ MOZ_ASSERT(&rhs != this, "self-moves are prohibited");
+ mInitialized = rhs.mInitialized;
+ mShmem = Move(rhs.mShmem);
+ return *this;
+ }
+
+ // No copies allowed
+ ShmemBuffer(const ShmemBuffer&) = delete;
+ ShmemBuffer& operator=(const ShmemBuffer&) = delete;
+
+ bool Valid() {
+ return mInitialized;
+ }
+
+ char* GetBytes() {
+ return mShmem.get<char>();
+ }
+
+ mozilla::ipc::Shmem& Get() {
+ return mShmem;
+ }
+
+private:
+ friend class ShmemPool;
+
+ bool mInitialized;
+ mozilla::ipc::Shmem mShmem;
+};
+
+class ShmemPool {
+public:
+ explicit ShmemPool(size_t aPoolSize);
+ ~ShmemPool();
+ // Get/GetIfAvailable differ in what thread they can run on. GetIfAvailable
+ // can run anywhere but won't allocate if the right size isn't available.
+ ShmemBuffer GetIfAvailable(size_t aSize);
+ void Put(ShmemBuffer&& aShmem);
+
+ // We need to use the allocation/deallocation functions
+ // of a specific IPC child/parent instance.
+ template <class T>
+ void Cleanup(T* aInstance)
+ {
+ MutexAutoLock lock(mMutex);
+ for (size_t i = 0; i < mShmemPool.Length(); i++) {
+ if (mShmemPool[i].mInitialized) {
+ aInstance->DeallocShmem(mShmemPool[i].Get());
+ mShmemPool[i].mInitialized = false;
+ }
+ }
+ }
+
+ template <class T>
+ ShmemBuffer Get(T* aInstance, size_t aSize)
+ {
+ MutexAutoLock lock(mMutex);
+
+ // Pool is empty, don't block caller.
+ if (mPoolFree == 0) {
+ // This isn't initialized, so will be understood as an error.
+ return ShmemBuffer();
+ }
+
+ ShmemBuffer& res = mShmemPool[mPoolFree - 1];
+
+ if (!res.mInitialized) {
+ LOG(("Initializing new Shmem in pool"));
+ if (!aInstance->AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &res.mShmem)) {
+ LOG(("Failure allocating new Shmem buffer"));
+ return ShmemBuffer();
+ }
+ res.mInitialized = true;
+ }
+
+ MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable?");
+
+ // Prepare buffer, increase size if needed (we never shrink as we don't
+ // maintain seperate sized pools and we don't want to keep reallocating)
+ if (res.mShmem.Size<char>() < aSize) {
+ LOG(("Size change/increase in Shmem Pool"));
+ aInstance->DeallocShmem(res.mShmem);
+ res.mInitialized = false;
+ // this may fail; always check return value
+ if (!aInstance->AllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &res.mShmem)) {
+ LOG(("Failure allocating resized Shmem buffer"));
+ return ShmemBuffer();
+ } else {
+ res.mInitialized = true;
+ }
+ }
+
+ MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable post resize?");
+
+ mPoolFree--;
+#ifdef DEBUG
+ size_t poolUse = mShmemPool.Length() - mPoolFree;
+ if (poolUse > mMaxPoolUse) {
+ mMaxPoolUse = poolUse;
+ LOG(("Maximum ShmemPool use increased: %d buffers", mMaxPoolUse));
+ }
+#endif
+ return Move(res);
+ }
+
+private:
+ Mutex mMutex;
+ size_t mPoolFree;
+#ifdef DEBUG
+ size_t mMaxPoolUse;
+#endif
+ nsTArray<ShmemBuffer> mShmemPool;
+};
+
+
+} // namespace mozilla
+
+#endif // mozilla_ShmemPool_h
diff --git a/dom/media/systemservices/moz.build b/dom/media/systemservices/moz.build
new file mode 100644
index 000000000..33e5ed1f1
--- /dev/null
+++ b/dom/media/systemservices/moz.build
@@ -0,0 +1,104 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+if CONFIG['MOZ_WEBRTC']:
+ EXPORTS += [
+ 'CamerasChild.h',
+ 'CamerasParent.h',
+ 'LoadManager.h',
+ 'LoadManagerFactory.h',
+ 'LoadMonitor.h',
+ ]
+ UNIFIED_SOURCES += [
+ 'CamerasChild.cpp',
+ 'CamerasParent.cpp',
+ 'LoadManager.cpp',
+ 'LoadManagerFactory.cpp',
+ 'LoadMonitor.cpp',
+ 'ShmemPool.cpp',
+ ]
+ LOCAL_INCLUDES += [
+ '/media/webrtc/signaling',
+ '/media/webrtc/trunk',
+ ]
+if CONFIG['OS_TARGET'] == 'WINNT':
+ DEFINES['WEBRTC_WIN'] = True
+else:
+ DEFINES['WEBRTC_POSIX'] = True
+
+
+if CONFIG['OS_TARGET'] == 'Android':
+ EXPORTS += [
+ 'OpenSLESProvider.h'
+ ]
+ UNIFIED_SOURCES += [
+ 'OpenSLESProvider.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += ['OSXRunLoopSingleton.cpp']
+ EXPORTS += ['OSXRunLoopSingleton.h']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ if CONFIG['ANDROID_VERSION'] >= '17':
+ LOCAL_INCLUDES += [
+ '%' + '%s/frameworks/wilhelm/include' % CONFIG['ANDROID_SOURCE'],
+ ]
+ else:
+ LOCAL_INCLUDES += [
+ '%' + '%s/system/media/wilhelm/include' % CONFIG['ANDROID_SOURCE'],
+ ]
+
+if CONFIG['_MSC_VER']:
+ DEFINES['__PRETTY_FUNCTION__'] = '__FUNCSIG__'
+
+ # This is intended as a temporary workaround to enable building with VS2015.
+ # media\webrtc\trunk\webrtc/base/criticalsection.h(59): warning C4312:
+ # 'reinterpret_cast': conversion from 'DWORD' to 'HANDLE' of greater size
+ CXXFLAGS += ['-wd4312']
+
+EXPORTS.mozilla += ['ShmemPool.h',]
+
+EXPORTS.mozilla.media += ['CamerasTypes.h',
+ 'DeviceChangeCallback.h',
+ 'MediaChild.h',
+ 'MediaParent.h',
+ 'MediaSystemResourceClient.h',
+ 'MediaSystemResourceManager.h',
+ 'MediaSystemResourceManagerChild.h',
+ 'MediaSystemResourceManagerParent.h',
+ 'MediaSystemResourceMessageUtils.h',
+ 'MediaSystemResourceService.h',
+ 'MediaSystemResourceTypes.h',
+ 'MediaTaskUtils.h',
+ 'MediaUtils.h',
+]
+UNIFIED_SOURCES += [
+ 'MediaChild.cpp',
+ 'MediaParent.cpp',
+ 'MediaSystemResourceClient.cpp',
+ 'MediaSystemResourceManager.cpp',
+ 'MediaSystemResourceManagerChild.cpp',
+ 'MediaSystemResourceManagerParent.cpp',
+ 'MediaSystemResourceService.cpp',
+ 'MediaUtils.cpp',
+]
+IPDL_SOURCES += [
+ 'PCameras.ipdl',
+ 'PMedia.ipdl',
+ 'PMediaSystemResourceManager.ipdl',
+]
+# /dom/base needed for nsGlobalWindow.h in MediaChild.cpp
+LOCAL_INCLUDES += [
+ '/dom/base',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']