mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
Bug 1582637 - Move internal DeviceChange events to higher order functions. r=jib,achronop
This does three major things: 1) Moves the DeviceChange events from manual callbacks/listeners to MediaEventSource/MediaEventListener. This is the reason this patch is so large, as it traverses a lot of files. There are four layers (from low to high): - CamerasChild for camera device list changes, and CubebDeviceEnumerator for microphone and speaker device list changes - MediaEngineWebRTC, which gathers these into a single listener - MediaManager, which owns the MediaEngineWebRTC backend - MediaDevices, where the events from MediaManager are exposed to js 2) Changes the fake event triggering from starting a 30-event burst on setting the js event listener, to a toggle, so that while the pref is on the events keep coming. 3) Moves the fake event generation from CamerasChild to MediaEngineWebRTC, since that's the lowest level where we are aware of both video and audio events. The fake event generation is also greatly simplified. From being a dedicated thread with periodic runnables, it is now a periodic timer on main thread that fires while fake events are enabled. MediaEventProducer gracefully handles thread safety. Differential Revision: https://phabricator.services.mozilla.com/D48516 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
94356644ac
commit
b89a421cf4
@ -48,12 +48,7 @@ class FuzzTimerCallBack final : public nsITimerCallback, public nsINamed {
|
||||
|
||||
NS_IMPL_ISUPPORTS(FuzzTimerCallBack, nsITimerCallback, nsINamed)
|
||||
|
||||
MediaDevices::~MediaDevices() {
|
||||
MediaManager* mediamanager = MediaManager::GetIfExists();
|
||||
if (mediamanager) {
|
||||
mediamanager->RemoveDeviceChangeCallback(this);
|
||||
}
|
||||
}
|
||||
MediaDevices::~MediaDevices() { mDeviceChangeListener.DisconnectIfExists(); }
|
||||
|
||||
static bool IsSameOriginWithAllParentDocs(nsINode* aDoc) {
|
||||
MOZ_ASSERT(aDoc);
|
||||
@ -253,16 +248,36 @@ mozilla::dom::EventHandlerNonNull* MediaDevices::GetOndevicechange() {
|
||||
return GetEventHandler(nsGkAtoms::ondevicechange);
|
||||
}
|
||||
|
||||
void MediaDevices::SetupDeviceChangeListener() {
|
||||
if (mIsDeviceChangeListenerSetUp) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner* window = GetOwner();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsISerialEventTarget* mainThread =
|
||||
window->EventTargetFor(TaskCategory::Other);
|
||||
if (!mainThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDeviceChangeListener = MediaManager::Get()->DeviceListChangeEvent().Connect(
|
||||
mainThread, this, &MediaDevices::OnDeviceChange);
|
||||
mIsDeviceChangeListenerSetUp = true;
|
||||
}
|
||||
|
||||
void MediaDevices::SetOndevicechange(
|
||||
mozilla::dom::EventHandlerNonNull* aCallback) {
|
||||
SetEventHandler(nsGkAtoms::ondevicechange, aCallback);
|
||||
|
||||
MediaManager::Get()->AddDeviceChangeCallback(this);
|
||||
SetupDeviceChangeListener();
|
||||
}
|
||||
|
||||
void MediaDevices::EventListenerAdded(nsAtom* aType) {
|
||||
MediaManager::Get()->AddDeviceChangeCallback(this);
|
||||
DOMEventTargetHelper::EventListenerAdded(aType);
|
||||
SetupDeviceChangeListener();
|
||||
}
|
||||
|
||||
JSObject* MediaDevices::WrapObject(JSContext* aCx,
|
||||
|
@ -5,12 +5,12 @@
|
||||
#ifndef mozilla_dom_MediaDevices_h
|
||||
#define mozilla_dom_MediaDevices_h
|
||||
|
||||
#include "MediaEventSource.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/media/DeviceChangeCallback.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -27,8 +27,7 @@ struct MediaTrackSupportedConstraints;
|
||||
} \
|
||||
}
|
||||
|
||||
class MediaDevices final : public DOMEventTargetHelper,
|
||||
public DeviceChangeCallback {
|
||||
class MediaDevices final : public DOMEventTargetHelper {
|
||||
public:
|
||||
explicit MediaDevices(nsPIDOMWindowInner* aWindow)
|
||||
: DOMEventTargetHelper(aWindow) {}
|
||||
@ -53,10 +52,12 @@ class MediaDevices final : public DOMEventTargetHelper,
|
||||
const DisplayMediaStreamConstraints& aConstraints, CallerType aCallerType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual void OnDeviceChange() override;
|
||||
// Called when MediaManager encountered a change in its device lists.
|
||||
void OnDeviceChange();
|
||||
|
||||
void SetupDeviceChangeListener();
|
||||
|
||||
mozilla::dom::EventHandlerNonNull* GetOndevicechange();
|
||||
|
||||
void SetOndevicechange(mozilla::dom::EventHandlerNonNull* aCallback);
|
||||
|
||||
void EventListenerAdded(nsAtom* aType) override;
|
||||
@ -70,6 +71,10 @@ class MediaDevices final : public DOMEventTargetHelper,
|
||||
virtual ~MediaDevices();
|
||||
nsCOMPtr<nsITimer> mFuzzTimer;
|
||||
|
||||
// Connect/Disconnect on main thread only
|
||||
MediaEventListener mDeviceChangeListener;
|
||||
bool mIsDeviceChangeListenerSetUp = false;
|
||||
|
||||
void RecordAccessTelemetry(const UseCounter counter) const;
|
||||
};
|
||||
|
||||
|
@ -2155,84 +2155,64 @@ nsresult MediaManager::NotifyRecordingStatusChange(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback) {
|
||||
bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
|
||||
MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() {
|
||||
MediaManager* manager = MediaManager::GetIfExists();
|
||||
MOZ_RELEASE_ASSERT(manager); // Must exist while media thread is alive
|
||||
// this is needed in case persistent permission is given but no gUM()
|
||||
// or enumeration() has created the real backend yet
|
||||
manager->GetBackend();
|
||||
if (fakeDeviceChangeEventOn)
|
||||
manager->GetBackend()->SetFakeDeviceChangeEvents();
|
||||
}));
|
||||
void MediaManager::DeviceListChanged() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sHasShutdown) {
|
||||
return;
|
||||
}
|
||||
mDeviceListChangeEvent.Notify();
|
||||
|
||||
return DeviceChangeNotifier::AddDeviceChangeCallback(aCallback);
|
||||
}
|
||||
// On some Windows machines, if we call EnumerateRawDevices immediately after
|
||||
// receiving devicechange event, we would get an outdated devices list.
|
||||
PR_Sleep(PR_MillisecondsToInterval(200));
|
||||
auto devices = MakeRefPtr<MediaDeviceSetRefCnt>();
|
||||
EnumerateRawDevices(0, MediaSourceEnum::Camera, MediaSourceEnum::Microphone,
|
||||
MediaSinkEnum::Speaker, DeviceEnumerationType::Normal,
|
||||
DeviceEnumerationType::Normal, false, devices)
|
||||
->Then(
|
||||
GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[self = RefPtr<MediaManager>(this), this, devices](bool) {
|
||||
if (!MediaManager::GetIfExists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void MediaManager::OnDeviceChange() {
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"MediaManager::OnDeviceChange", [self = RefPtr<MediaManager>(this)]() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (sHasShutdown) {
|
||||
return;
|
||||
}
|
||||
self->NotifyDeviceChange();
|
||||
nsTArray<nsString> deviceIDs;
|
||||
|
||||
// On some Windows machine, if we call EnumerateRawDevices immediately
|
||||
// after receiving devicechange event, sometimes we would get outdated
|
||||
// devices list.
|
||||
PR_Sleep(PR_MillisecondsToInterval(200));
|
||||
auto devices = MakeRefPtr<MediaDeviceSetRefCnt>();
|
||||
self->EnumerateRawDevices(
|
||||
0, MediaSourceEnum::Camera, MediaSourceEnum::Microphone,
|
||||
MediaSinkEnum::Speaker, DeviceEnumerationType::Normal,
|
||||
DeviceEnumerationType::Normal, false, devices)
|
||||
->Then(
|
||||
GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[self, devices](bool) {
|
||||
if (!MediaManager::GetIfExists()) {
|
||||
return;
|
||||
}
|
||||
for (auto& device : *devices) {
|
||||
nsString id;
|
||||
device->GetId(id);
|
||||
id.ReplaceSubstring(NS_LITERAL_STRING("default: "),
|
||||
NS_LITERAL_STRING(""));
|
||||
if (!deviceIDs.Contains(id)) {
|
||||
deviceIDs.AppendElement(id);
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsString> deviceIDs;
|
||||
for (auto& id : mDeviceIDs) {
|
||||
if (deviceIDs.Contains(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& device : *devices) {
|
||||
nsString id;
|
||||
device->GetId(id);
|
||||
id.ReplaceSubstring(NS_LITERAL_STRING("default: "),
|
||||
NS_LITERAL_STRING(""));
|
||||
if (!deviceIDs.Contains(id)) {
|
||||
deviceIDs.AppendElement(id);
|
||||
}
|
||||
}
|
||||
// Stop the coresponding SourceListener
|
||||
nsGlobalWindowInner::InnerWindowByIdTable* windowsById =
|
||||
nsGlobalWindowInner::GetWindowsTable();
|
||||
if (!windowsById) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& id : self->mDeviceIDs) {
|
||||
if (deviceIDs.Contains(id)) {
|
||||
continue;
|
||||
}
|
||||
for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsGlobalWindowInner* window = iter.Data();
|
||||
IterateWindowListeners(
|
||||
window,
|
||||
[&id](const RefPtr<GetUserMediaWindowListener>& aListener) {
|
||||
aListener->StopRawID(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the coresponding SourceListener
|
||||
nsGlobalWindowInner::InnerWindowByIdTable* windowsById =
|
||||
nsGlobalWindowInner::GetWindowsTable();
|
||||
if (!windowsById) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto iter = windowsById->Iter(); !iter.Done();
|
||||
iter.Next()) {
|
||||
nsGlobalWindowInner* window = iter.Data();
|
||||
self->IterateWindowListeners(
|
||||
window,
|
||||
[&id](const RefPtr<GetUserMediaWindowListener>&
|
||||
aListener) { aListener->StopRawID(id); });
|
||||
}
|
||||
}
|
||||
|
||||
self->mDeviceIDs = deviceIDs;
|
||||
},
|
||||
[](RefPtr<MediaMgrError>&& reason) {});
|
||||
}));
|
||||
mDeviceIDs = deviceIDs;
|
||||
},
|
||||
[](RefPtr<MediaMgrError>&& reason) {});
|
||||
}
|
||||
|
||||
nsresult MediaManager::GenerateUUID(nsAString& aResult) {
|
||||
@ -3363,7 +3343,8 @@ MediaEngine* MediaManager::GetBackend() {
|
||||
#else
|
||||
mBackend = new MediaEngineDefault();
|
||||
#endif
|
||||
mBackend->AddDeviceChangeCallback(this);
|
||||
mDeviceListChangeListener = mBackend->DeviceListChangeEvent().Connect(
|
||||
AbstractThread::MainThread(), this, &MediaManager::DeviceListChanged);
|
||||
}
|
||||
return mBackend;
|
||||
}
|
||||
@ -3398,24 +3379,6 @@ void MediaManager::OnNavigation(uint64_t aWindowID) {
|
||||
RemoveWindowID(aWindowID);
|
||||
}
|
||||
MOZ_ASSERT(!GetWindowListener(aWindowID));
|
||||
|
||||
RemoveMediaDevicesCallback(aWindowID);
|
||||
}
|
||||
|
||||
void MediaManager::RemoveMediaDevicesCallback(uint64_t aWindowID) {
|
||||
MutexAutoLock lock(mCallbackMutex);
|
||||
for (DeviceChangeCallback* observer : mDeviceChangeCallbackList) {
|
||||
MediaDevices* mediadevices = static_cast<MediaDevices*>(observer);
|
||||
MOZ_ASSERT(mediadevices);
|
||||
if (mediadevices) {
|
||||
nsPIDOMWindowInner* window = mediadevices->GetOwner();
|
||||
MOZ_ASSERT(window);
|
||||
if (window && window->WindowID() == aWindowID) {
|
||||
RemoveDeviceChangeCallbackLocked(observer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaManager::AddWindowID(uint64_t aWindowId,
|
||||
@ -3509,8 +3472,22 @@ void MediaManager::GetPrefs(nsIPrefBranch* aBranch, const char* aData) {
|
||||
GetPrefBool(aBranch, "media.getusermedia.aec_aec_delay_agnostic", aData,
|
||||
&mPrefs.mDelayAgnostic);
|
||||
GetPref(aBranch, "media.getusermedia.channels", aData, &mPrefs.mChannels);
|
||||
bool oldFakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
|
||||
GetPrefBool(aBranch, "media.ondevicechange.fakeDeviceChangeEvent.enabled",
|
||||
aData, &mPrefs.mFakeDeviceChangeEventOn);
|
||||
if (mPrefs.mFakeDeviceChangeEventOn != oldFakeDeviceChangeEventOn) {
|
||||
// Dispatch directly to the media thread since we're guaranteed to not be in
|
||||
// shutdown here. This is called either on construction, or when a pref has
|
||||
// changed. The pref observers are disconnected during shutdown.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!sHasShutdown);
|
||||
mMediaThread->message_loop()->PostTask(NS_NewRunnableFunction(
|
||||
"MediaManager::SetFakeDeviceChangeEventsEnabled",
|
||||
[enable = mPrefs.mFakeDeviceChangeEventOn] {
|
||||
if (MediaManager* mm = MediaManager::GetIfExists()) {
|
||||
mm->GetBackend()->SetFakeDeviceChangeEventsEnabled(enable);
|
||||
}
|
||||
}));
|
||||
}
|
||||
#endif
|
||||
GetPrefBool(aBranch, "media.navigator.audio.full_duplex", aData,
|
||||
&mPrefs.mFullDuplex);
|
||||
@ -3601,8 +3578,9 @@ void MediaManager::Shutdown() {
|
||||
// started it from!
|
||||
{
|
||||
if (mManager->mBackend) {
|
||||
mManager->mBackend->Shutdown(); // ok to invoke multiple times
|
||||
mManager->mBackend->RemoveDeviceChangeCallback(mManager);
|
||||
mManager->mBackend->SetFakeDeviceChangeEventsEnabled(false);
|
||||
mManager->mBackend->Shutdown(); // idempotent
|
||||
mManager->mDeviceListChangeListener.DisconnectIfExists();
|
||||
}
|
||||
}
|
||||
mozilla::ipc::BackgroundChild::CloseForCurrentThread();
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
#include "MediaEngine.h"
|
||||
#include "MediaEnginePrefs.h"
|
||||
#include "mozilla/media/DeviceChangeCallback.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "mozilla/dom/GetUserMediaRequest.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsIMediaManager.h"
|
||||
@ -130,10 +130,7 @@ typedef nsRefPtrHashtable<nsUint64HashKey, GetUserMediaWindowListener>
|
||||
WindowTable;
|
||||
typedef MozPromise<RefPtr<AudioDeviceInfo>, nsresult, true> SinkInfoPromise;
|
||||
|
||||
class MediaManager final : public nsIMediaManagerService,
|
||||
public nsIObserver,
|
||||
public DeviceChangeNotifier,
|
||||
public DeviceChangeCallback {
|
||||
class MediaManager final : public nsIMediaManagerService, public nsIObserver {
|
||||
friend SourceListener;
|
||||
|
||||
public:
|
||||
@ -256,10 +253,11 @@ class MediaManager final : public nsIMediaManagerService,
|
||||
void OnNavigation(uint64_t aWindowID);
|
||||
bool IsActivelyCapturingOrHasAPermission(uint64_t aWindowId);
|
||||
|
||||
MediaEnginePrefs mPrefs;
|
||||
MediaEventSource<void>& DeviceListChangeEvent() {
|
||||
return mDeviceListChangeEvent;
|
||||
}
|
||||
|
||||
virtual int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
|
||||
virtual void OnDeviceChange() override;
|
||||
MediaEnginePrefs mPrefs;
|
||||
|
||||
private:
|
||||
static nsresult GenerateUUID(nsAString& aResult);
|
||||
@ -334,6 +332,7 @@ class MediaManager final : public nsIMediaManagerService,
|
||||
|
||||
void StopMediaStreams();
|
||||
void RemoveMediaDevicesCallback(uint64_t aWindowID);
|
||||
void DeviceListChanged();
|
||||
|
||||
// ONLY access from MainThread so we don't need to lock
|
||||
WindowTable mActiveWindows;
|
||||
@ -353,6 +352,11 @@ class MediaManager final : public nsIMediaManagerService,
|
||||
|
||||
nsTArray<nsString> mDeviceIDs;
|
||||
|
||||
// Connect/Disconnect on media thread only
|
||||
MediaEventListener mDeviceListChangeListener;
|
||||
|
||||
MediaEventProducer<void> mDeviceListChangeEvent;
|
||||
|
||||
public:
|
||||
RefPtr<media::Parent<media::NonE10s>> mNonE10sParent;
|
||||
};
|
||||
|
@ -24,9 +24,6 @@ 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 {
|
||||
|
||||
@ -34,41 +31,12 @@ CamerasSingleton::CamerasSingleton()
|
||||
: mCamerasMutex("CamerasSingleton::mCamerasMutex"),
|
||||
mCameras(nullptr),
|
||||
mCamerasChildThread(nullptr),
|
||||
mFakeDeviceChangeEventThread(nullptr),
|
||||
mInShutdown(false) {
|
||||
LOG(("CamerasSingleton: %p", this));
|
||||
}
|
||||
|
||||
CamerasSingleton::~CamerasSingleton() { LOG(("~CamerasSingleton: %p", this)); }
|
||||
|
||||
class FakeOnDeviceChangeEventRunnable : public Runnable {
|
||||
public:
|
||||
explicit FakeOnDeviceChangeEventRunnable(uint8_t counter)
|
||||
: Runnable("camera::FakeOnDeviceChangeEventRunnable"),
|
||||
mCounter(counter) {}
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
|
||||
|
||||
CamerasChild* child = CamerasSingleton::Child();
|
||||
if (child) {
|
||||
child->NotifyDeviceChange();
|
||||
|
||||
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()
|
||||
@ -140,23 +108,6 @@ CamerasChild* GetCamerasChildIfExists() {
|
||||
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 DeviceChangeNotifier::AddDeviceChangeCallback(aCallback);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult CamerasChild::RecvReplyFailure(void) {
|
||||
LOG(("%s", __PRETTY_FUNCTION__));
|
||||
MonitorAutoLock monitor(mReplyMonitor);
|
||||
@ -554,15 +505,6 @@ void CamerasChild::ShutdownChild() {
|
||||
LOG(("Erasing sCameras & thread refs (original thread)"));
|
||||
CamerasSingleton::Child() = nullptr;
|
||||
CamerasSingleton::Thread() = nullptr;
|
||||
|
||||
if (CamerasSingleton::FakeDeviceChangeEventThread()) {
|
||||
RefPtr<ShutdownRunnable> runnable = new ShutdownRunnable(NewRunnableMethod(
|
||||
"nsIThread::Shutdown", CamerasSingleton::FakeDeviceChangeEventThread(),
|
||||
&nsIThread::Shutdown));
|
||||
CamerasSingleton::FakeDeviceChangeEventThread()->Dispatch(
|
||||
runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
CamerasSingleton::FakeDeviceChangeEventThread() = nullptr;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult CamerasChild::RecvDeliverFrame(
|
||||
@ -580,33 +522,10 @@ mozilla::ipc::IPCResult CamerasChild::RecvDeliverFrame(
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult CamerasChild::RecvDeviceChange() {
|
||||
this->NotifyDeviceChange();
|
||||
mDeviceListChangeEvent.Notify();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void CamerasChild::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
MonitorAutoLock monitor(mReplyMonitor);
|
||||
mIPCIsAlive = false;
|
||||
|
@ -11,8 +11,8 @@
|
||||
#include "mozilla/Pair.h"
|
||||
#include "mozilla/camera/PCamerasChild.h"
|
||||
#include "mozilla/camera/PCamerasParent.h"
|
||||
#include "mozilla/media/DeviceChangeCallback.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
// conflicts with #include of scoped_ptr.h
|
||||
@ -81,11 +81,6 @@ class CamerasSingleton {
|
||||
return singleton().mCamerasChildThread;
|
||||
}
|
||||
|
||||
static nsCOMPtr<nsIThread>& FakeDeviceChangeEventThread() {
|
||||
Mutex().AssertCurrentThreadOwns();
|
||||
return singleton().mFakeDeviceChangeEventThread;
|
||||
}
|
||||
|
||||
static bool InShutdown() { return singleton().mInShutdown; }
|
||||
|
||||
static void StartShutdown() { singleton().mInShutdown = true; }
|
||||
@ -112,7 +107,6 @@ class CamerasSingleton {
|
||||
// will be before actual destruction.
|
||||
CamerasChild* mCameras;
|
||||
nsCOMPtr<nsIThread> mCamerasChildThread;
|
||||
nsCOMPtr<nsIThread> mFakeDeviceChangeEventThread;
|
||||
Atomic<bool> mInShutdown;
|
||||
};
|
||||
|
||||
@ -143,7 +137,7 @@ int GetChildAndCall(MEM_FUN&& f, ARGS&&... args) {
|
||||
}
|
||||
}
|
||||
|
||||
class CamerasChild final : public PCamerasChild, public DeviceChangeNotifier {
|
||||
class CamerasChild final : public PCamerasChild {
|
||||
friend class mozilla::ipc::BackgroundChildImpl;
|
||||
template <class T>
|
||||
friend class mozilla::camera::LockAndDispatch;
|
||||
@ -160,8 +154,6 @@ class CamerasChild final : public PCamerasChild, public DeviceChangeNotifier {
|
||||
const VideoFrameProperties& prop) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvDeviceChange() override;
|
||||
int AddDeviceChangeCallback(DeviceChangeCallback* aCallback) override;
|
||||
int SetFakeDeviceChangeEvents();
|
||||
|
||||
// these are response messages to our outgoing requests
|
||||
mozilla::ipc::IPCResult RecvReplyNumberOfCaptureDevices(const int&) override;
|
||||
@ -204,6 +196,27 @@ class CamerasChild final : public PCamerasChild, public DeviceChangeNotifier {
|
||||
void ShutdownAll();
|
||||
int EnsureInitialized(CaptureEngine aCapEngine);
|
||||
|
||||
template <typename This>
|
||||
int ConnectDeviceListChangeListener(MediaEventListener* aListener,
|
||||
AbstractThread* aTarget, This* aThis,
|
||||
void (This::*aMethod)()) {
|
||||
// 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
|
||||
// CamerasParent api, e.g., RecvNumberOfCaptureDevices(), is called.
|
||||
|
||||
// So here we setup camera engine via EnsureInitialized(aCapEngine)
|
||||
|
||||
EnsureInitialized(CameraEngine);
|
||||
*aListener = mDeviceListChangeEvent.Connect(aTarget, aThis, aMethod);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
FrameRelay* Callback(CaptureEngine aCapEngine, int capture_id);
|
||||
|
||||
private:
|
||||
@ -245,6 +258,7 @@ class CamerasChild final : public PCamerasChild, public DeviceChangeNotifier {
|
||||
nsCString mReplyDeviceName;
|
||||
nsCString mReplyDeviceID;
|
||||
bool mReplyScary;
|
||||
MediaEventProducer<void> mDeviceListChangeEvent;
|
||||
};
|
||||
|
||||
} // namespace camera
|
||||
|
@ -1,63 +0,0 @@
|
||||
/* -*- 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
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class DeviceChangeCallback {
|
||||
public:
|
||||
virtual ~DeviceChangeCallback() = default;
|
||||
virtual void OnDeviceChange() = 0;
|
||||
};
|
||||
|
||||
class DeviceChangeNotifier {
|
||||
public:
|
||||
DeviceChangeNotifier()
|
||||
: mCallbackMutex("mozilla::DeviceChangeCallback::mCallbackMutex") {}
|
||||
|
||||
void NotifyDeviceChange() {
|
||||
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;
|
||||
}
|
||||
|
||||
int RemoveDeviceChangeCallback(DeviceChangeCallback* aCallback) {
|
||||
MutexAutoLock lock(mCallbackMutex);
|
||||
return RemoveDeviceChangeCallbackLocked(aCallback);
|
||||
}
|
||||
|
||||
int RemoveDeviceChangeCallbackLocked(DeviceChangeCallback* aCallback) {
|
||||
mCallbackMutex.AssertCurrentThreadOwns();
|
||||
if (mDeviceChangeCallbackList.IndexOf(aCallback) !=
|
||||
mDeviceChangeCallbackList.NoIndex)
|
||||
mDeviceChangeCallbackList.RemoveElement(aCallback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual ~DeviceChangeNotifier() = default;
|
||||
|
||||
protected:
|
||||
nsTArray<DeviceChangeCallback*> mDeviceChangeCallbackList;
|
||||
Mutex mCallbackMutex;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_DeviceChangeCallback_h
|
@ -50,7 +50,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
EXPORTS.mozilla += ['ShmemPool.h',]
|
||||
|
||||
EXPORTS.mozilla.media += ['CamerasTypes.h',
|
||||
'DeviceChangeCallback.h',
|
||||
'MediaChild.h',
|
||||
'MediaParent.h',
|
||||
'MediaSystemResourceClient.h',
|
||||
|
@ -310,17 +310,13 @@ void CubebDeviceEnumerator::AudioDeviceListChanged(Side aSide) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (aSide == Side::INPUT) {
|
||||
mInputDevices.Clear();
|
||||
mOnInputDeviceListChange.Notify();
|
||||
} else {
|
||||
MOZ_ASSERT(aSide == Side::OUTPUT);
|
||||
mOutputDevices.Clear();
|
||||
mOnOutputDeviceListChange.Notify();
|
||||
}
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(
|
||||
NS_NewRunnableFunction("CubebDeviceEnumerator::AudioDeviceListChanged",
|
||||
[self = RefPtr<CubebDeviceEnumerator>(this)]() {
|
||||
self->NotifyDeviceChange();
|
||||
}));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -8,14 +8,14 @@
|
||||
#include "AudioDeviceInfo.h"
|
||||
#include "CubebUtils.h"
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "mozilla/media/DeviceChangeCallback.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
// This class implements a cache for accessing the audio device list.
|
||||
// It can be accessed on any thread.
|
||||
class CubebDeviceEnumerator final : public DeviceChangeNotifier {
|
||||
class CubebDeviceEnumerator final {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CubebDeviceEnumerator)
|
||||
|
||||
@ -44,6 +44,15 @@ class CubebDeviceEnumerator final : public DeviceChangeNotifier {
|
||||
};
|
||||
already_AddRefed<AudioDeviceInfo> DeviceInfoFromName(const nsString& aName,
|
||||
Side aSide);
|
||||
// Event source to listen for changes to the audio input device list on.
|
||||
MediaEventSource<void>& OnAudioInputDeviceListChange() {
|
||||
return mOnInputDeviceListChange;
|
||||
}
|
||||
|
||||
// Event source to listen for changes to the audio output device list on.
|
||||
MediaEventSource<void>& OnAudioOutputDeviceListChange() {
|
||||
return mOnOutputDeviceListChange;
|
||||
}
|
||||
|
||||
private:
|
||||
CubebDeviceEnumerator();
|
||||
@ -66,6 +75,8 @@ class CubebDeviceEnumerator final : public DeviceChangeNotifier {
|
||||
// cubeb itself. Set in the constructor and then can be access on any thread.
|
||||
bool mManualInputInvalidation;
|
||||
bool mManualOutputInvalidation;
|
||||
MediaEventProducer<void> mOnInputDeviceListChange;
|
||||
MediaEventProducer<void> mOnOutputDeviceListChange;
|
||||
// The singleton instance.
|
||||
static StaticRefPtr<CubebDeviceEnumerator> sInstance;
|
||||
};
|
||||
|
@ -6,11 +6,11 @@
|
||||
#define MEDIAENGINE_H_
|
||||
|
||||
#include "DOMMediaStream.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaTrackGraph.h"
|
||||
#include "MediaTrackConstraints.h"
|
||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
#include "mozilla/media/DeviceChangeCallback.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ThreadSafeWeakPtr.h"
|
||||
|
||||
@ -30,7 +30,7 @@ enum MediaSinkEnum {
|
||||
|
||||
enum { kVideoTrack = 1, kAudioTrack = 2, kTrackCount };
|
||||
|
||||
class MediaEngine : public DeviceChangeNotifier, public DeviceChangeCallback {
|
||||
class MediaEngine {
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaEngine)
|
||||
NS_DECL_OWNINGTHREAD
|
||||
@ -47,9 +47,9 @@ class MediaEngine : public DeviceChangeNotifier, public DeviceChangeCallback {
|
||||
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void SetFakeDeviceChangeEvents() {}
|
||||
virtual void SetFakeDeviceChangeEventsEnabled(bool aEnable) {}
|
||||
|
||||
void OnDeviceChange() override { NotifyDeviceChange(); }
|
||||
virtual MediaEventSource<void>& DeviceListChangeEvent() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~MediaEngine() = default;
|
||||
|
@ -144,8 +144,13 @@ class MediaEngineDefault : public MediaEngine {
|
||||
nsTArray<RefPtr<MediaDevice>>*) override;
|
||||
void Shutdown() override {}
|
||||
|
||||
MediaEventSource<void>& DeviceListChangeEvent() override {
|
||||
return mDeviceListChangeEvent;
|
||||
}
|
||||
|
||||
private:
|
||||
~MediaEngineDefault() = default;
|
||||
MediaEventProducer<void> mDeviceListChangeEvent;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "nsITabSource.h"
|
||||
#include "prenv.h"
|
||||
|
||||
#define FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS 5000
|
||||
|
||||
static mozilla::LazyLogModule sGetUserMediaLog("GetUserMedia");
|
||||
#undef LOG
|
||||
#define LOG(args) MOZ_LOG(sGetUserMediaLog, mozilla::LogLevel::Debug, args)
|
||||
@ -47,13 +49,40 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs& aPrefs)
|
||||
&mHasTabVideoSource);
|
||||
}
|
||||
|
||||
GetChildAndCall(&CamerasChild::AddDeviceChangeCallback, this);
|
||||
GetEnumerator()->AddDeviceChangeCallback(this);
|
||||
GetChildAndCall(
|
||||
&CamerasChild::ConnectDeviceListChangeListener<MediaEngineWebRTC>,
|
||||
&mCameraListChangeListener, AbstractThread::MainThread(), this,
|
||||
&MediaEngineWebRTC::DeviceListChanged);
|
||||
mMicrophoneListChangeListener =
|
||||
GetEnumerator()->OnAudioInputDeviceListChange().Connect(
|
||||
AbstractThread::MainThread(), this,
|
||||
&MediaEngineWebRTC::DeviceListChanged);
|
||||
mSpeakerListChangeListener =
|
||||
GetEnumerator()->OnAudioOutputDeviceListChange().Connect(
|
||||
AbstractThread::MainThread(), this,
|
||||
&MediaEngineWebRTC::DeviceListChanged);
|
||||
}
|
||||
|
||||
void MediaEngineWebRTC::SetFakeDeviceChangeEvents() {
|
||||
void MediaEngineWebRTC::SetFakeDeviceChangeEventsEnabled(bool aEnable) {
|
||||
AssertIsOnOwningThread();
|
||||
GetChildAndCall(&CamerasChild::SetFakeDeviceChangeEvents);
|
||||
|
||||
// To simulate the devicechange event in mochitest, we schedule a timer to
|
||||
// issue "devicechange" repeatedly until disabled.
|
||||
|
||||
if (aEnable && !mFakeDeviceChangeEventTimer) {
|
||||
NS_NewTimerWithFuncCallback(
|
||||
getter_AddRefs(mFakeDeviceChangeEventTimer),
|
||||
&FakeDeviceChangeEventTimerTick, this,
|
||||
FAKE_ONDEVICECHANGE_EVENT_PERIOD_IN_MS, nsITimer::TYPE_REPEATING_SLACK,
|
||||
"MediaEngineWebRTC::mFakeDeviceChangeEventTimer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aEnable && mFakeDeviceChangeEventTimer) {
|
||||
mFakeDeviceChangeEventTimer->Cancel();
|
||||
mFakeDeviceChangeEventTimer = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaEngineWebRTC::EnumerateVideoDevices(
|
||||
@ -284,13 +313,19 @@ void MediaEngineWebRTC::EnumerateDevices(
|
||||
|
||||
void MediaEngineWebRTC::Shutdown() {
|
||||
AssertIsOnOwningThread();
|
||||
if (camera::GetCamerasChildIfExists()) {
|
||||
GetChildAndCall(&CamerasChild::RemoveDeviceChangeCallback, this);
|
||||
}
|
||||
GetEnumerator()->RemoveDeviceChangeCallback(this);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mFakeDeviceChangeEventTimer);
|
||||
mCameraListChangeListener.DisconnectIfExists();
|
||||
mMicrophoneListChangeListener.DisconnectIfExists();
|
||||
mSpeakerListChangeListener.DisconnectIfExists();
|
||||
|
||||
LOG(("%s", __FUNCTION__));
|
||||
mozilla::camera::Shutdown();
|
||||
}
|
||||
|
||||
/* static */ void MediaEngineWebRTC::FakeDeviceChangeEventTimerTick(
|
||||
nsITimer* aTimer, void* aClosure) {
|
||||
MediaEngineWebRTC* self = static_cast<MediaEngineWebRTC*>(aClosure);
|
||||
self->DeviceListChanged();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -52,7 +52,9 @@ class MediaEngineWebRTC : public MediaEngine {
|
||||
public:
|
||||
explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
|
||||
|
||||
virtual void SetFakeDeviceChangeEvents() override;
|
||||
// Enable periodic fake "devicechange" event. Must always be called from the
|
||||
// same thread, and must be disabled before shutdown.
|
||||
void SetFakeDeviceChangeEventsEnabled(bool aEnable) override;
|
||||
|
||||
// Clients should ensure to clean-up sources video/audio sources
|
||||
// before invoking Shutdown on this class.
|
||||
@ -64,6 +66,10 @@ class MediaEngineWebRTC : public MediaEngine {
|
||||
void EnumerateDevices(uint64_t aWindowId, dom::MediaSourceEnum, MediaSinkEnum,
|
||||
nsTArray<RefPtr<MediaDevice>>*) override;
|
||||
|
||||
MediaEventSource<void>& DeviceListChangeEvent() override {
|
||||
return mDeviceListChangeEvent;
|
||||
}
|
||||
|
||||
private:
|
||||
~MediaEngineWebRTC() = default;
|
||||
void EnumerateVideoDevices(uint64_t aWindowId,
|
||||
@ -74,11 +80,20 @@ class MediaEngineWebRTC : public MediaEngine {
|
||||
void EnumerateSpeakerDevices(uint64_t aWindowId,
|
||||
nsTArray<RefPtr<MediaDevice>>*);
|
||||
|
||||
void DeviceListChanged() { mDeviceListChangeEvent.Notify(); }
|
||||
|
||||
static void FakeDeviceChangeEventTimerTick(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
const bool mDelayAgnostic;
|
||||
const bool mExtendedFilter;
|
||||
// This also is set in the ctor and then never changed, but we can't make it
|
||||
// const because we pass it to a function that takes bool* in the ctor.
|
||||
bool mHasTabVideoSource;
|
||||
MediaEventListener mCameraListChangeListener;
|
||||
MediaEventListener mMicrophoneListChangeListener;
|
||||
MediaEventListener mSpeakerListChangeListener;
|
||||
MediaEventProducer<void> mDeviceListChangeEvent;
|
||||
nsCOMPtr<nsITimer> mFakeDeviceChangeEventTimer;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
Loading…
x
Reference in New Issue
Block a user