https://bugs.webkit.org/show_bug.cgi?id=169872
<rdar://problem/
28945035>
Reviewed by Jer Noble.
Source/WebCore:
Test: fast/mediastream/device-change-event.html
* Modules/mediastream/MediaDevices.cpp:
(WebCore::MediaDevices::MediaDevices): Register for devicechange callbacks.
(WebCore::MediaDevices::~MediaDevices): Unregister.
(WebCore::MediaDevices::scheduledEventTimerFired):
* Modules/mediastream/MediaDevices.h:
* Modules/mediastream/MediaDevices.idl:
* Modules/mediastream/MediaDevicesEnumerationRequest.cpp:
(WebCore::MediaDevicesEnumerationRequest::setDeviceInfo): Remove unnecessary instance variables.
* Modules/mediastream/MediaDevicesEnumerationRequest.h:
* Modules/mediastream/MediaDevicesRequest.cpp:
(WebCore::MediaDevicesRequest::start): Remove m_idHashSalt, it isn't used. RealtimeMediaSourceCenter
now has the method to hash ids and group IDs.
(WebCore::hashString): Deleted.
(WebCore::MediaDevicesRequest::hashID): Deleted.
* Modules/mediastream/MediaDevicesRequest.h:
* dom/Document.h:
(WebCore::Document::setDeviceIDHashSalt):
(WebCore::Document::deviceIDHashSalt):
* dom/EventNames.h: Add devicechange.
* dom/EventTargetFactory.in: Add MediaDevices.
* html/HTMLAttributeNames.in: Add ondevicechange.
* platform/mediastream/CaptureDeviceManager.cpp:
(WebCore::CaptureDeviceManager::captureDeviceFromPersistentID): Renamed from captureDeviceFromDeviceID
and changed to return a std::optional<CaptureDevice>.
(WebCore::CaptureDeviceManager::captureDeviceFromDeviceID): Deleted.
* platform/mediastream/RealtimeMediaSourceCenter.cpp:
(WebCore::addStringToSHA): New, add string bytes to SHA1. Moved from MediaDevicesRequest
so we can use for in testing.
(WebCore::RealtimeMediaSourceCenter::hashStringWithSalt): Generate hash for a string with a
salt. Moved from MediaDevicesRequest so we can use for in testing.
(WebCore::RealtimeMediaSourceCenter::captureDeviceWithUniqueID): Find a CaptureDevice given
a unique ID and the process hash salt.
(WebCore::RealtimeMediaSourceCenter::setDeviceEnabled): Enable/disable a device. Used for
layout tests only.
(WebCore::RealtimeMediaSourceCenter::addDevicesChangedObserver): Add a devices changed listener.
(WebCore::RealtimeMediaSourceCenter::removeDevicesChangedObserver): Remove a listener.
(WebCore::RealtimeMediaSourceCenter::captureDevicesChanged): Notify listeners.
* platform/mediastream/RealtimeMediaSourceCenter.h:
* platform/mediastream/mac/AVCaptureDeviceManager.mm:
(WebCore::AVCaptureDeviceManager::addDevicesChangedObserver): Update for change to captureDeviceFromDeviceID.
* platform/mock/MockRealtimeMediaSource.cpp:
(WebCore::MockRealtimeMediaSource::audioDevices): All devices are enabled by default.
(WebCore::MockRealtimeMediaSource::videoDevices): Ditto.
* platform/mock/MockRealtimeMediaSourceCenter.cpp:
(WebCore::MockRealtimeMediaSourceCenter::createMediaStream): Drive-by cleanup: use the vector
of devices instead of making assumptions about the number.
(WebCore::MockRealtimeMediaSourceCenter::getMediaStreamDevices): Only include enabled devices.
(WebCore::MockRealtimeMediaSourceCenter::setDeviceEnabled): Enable or disable a device.
* platform/mock/MockRealtimeMediaSourceCenter.h:
* testing/Internals.cpp:
(WebCore::Internals::setMediaDeviceState): Enable or disable a mock capture device.
* testing/Internals.h:
* testing/Internals.idl:
LayoutTests:
* fast/mediastream/device-change-event-expected.txt: Added.
* fast/mediastream/device-change-event.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@215929
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2017-04-28 Eric Carlson <eric.carlson@apple.com>
+
+ Implement ondevicechange
+ https://bugs.webkit.org/show_bug.cgi?id=169872
+ <rdar://problem/28945035>
+
+ Reviewed by Jer Noble.
+
+ * fast/mediastream/device-change-event-expected.txt: Added.
+ * fast/mediastream/device-change-event.html: Added.
+
2017-04-28 Joanmarie Diggs <jdiggs@igalia.com>
AX: Implement aria-value support for focusable separators
--- /dev/null
+
+PASS 'devicechange' event fired when device list changes
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Testing local audio capture playback causes "playing" event to fire</title>
+ <script src="../../resources/testharness.js"></script>
+ <script src="../../resources/testharnessreport.js"></script>
+ <script>
+ let deviceIds = [];
+ promise_test((test) => {
+ if (window.testRunner)
+ testRunner.setUserMediaPermission(true);
+
+ return navigator.mediaDevices.enumerateDevices().then((devices) => {
+ return new Promise((resolve, reject) => {
+ devices.forEach(function(device) { deviceIds.push(device.deviceId); });
+ navigator.mediaDevices.ondevicechange = (event) => { resolve(event); };
+ setTimeout(() => resolve(new Event('devicechange')), 100);
+ });
+ }).then((event) => {
+ assert_equals(event.type, "devicechange", "correct event type");
+ return new Promise((resolve, reject) => {
+ return navigator.mediaDevices.enumerateDevices().then((devices) => {
+ let deviceIDs2 = [];
+ devices.forEach(function(device) { deviceIDs2.push(device.deviceId); });
+ try {
+ assert_true(deviceIDs2.indexOf(deviceIds[0]) != -1, "device not reported by enumerateDevices");
+ assert_equals(deviceIDs2.length, deviceIds.length, "correct number of devices");
+ } catch(e) {
+ reject(e);
+ return;
+ }
+
+ navigator.mediaDevices.ondevicechange = (event) => { resolve(event); };
+ setTimeout(() => resolve(new Event('devicechange')), 100);
+ });
+ });
+ }).then((event) => {
+ assert_equals(event.type, "devicechange", "correct event type");
+ return new Promise((resolve, reject) => {
+ return navigator.mediaDevices.enumerateDevices().then((devices) => {
+ let deviceIDs2 = [];
+ devices.forEach(function(device) { deviceIDs2.push(device.deviceId); });
+ try {
+ assert_in_array(deviceIds[0], deviceIDs2, "device reported by enumerateDevices again");
+ assert_equals(deviceIDs2.length, deviceIds.length, "correct number of devices");
+ } catch(e) {
+ reject(e);
+ return;
+ }
+
+ resolve();
+ });
+ });
+ });
+
+ }, "'devicechange' event fired when device list changes");
+ </script>
+</head>
+<body>
+</body>
+</html>
+2017-04-28 Eric Carlson <eric.carlson@apple.com>
+
+ Implement ondevicechange
+ https://bugs.webkit.org/show_bug.cgi?id=169872
+ <rdar://problem/28945035>
+
+ Reviewed by Jer Noble.
+
+ Test: fast/mediastream/device-change-event.html
+
+ * Modules/mediastream/MediaDevices.cpp:
+ (WebCore::MediaDevices::MediaDevices): Register for devicechange callbacks.
+ (WebCore::MediaDevices::~MediaDevices): Unregister.
+ (WebCore::MediaDevices::scheduledEventTimerFired):
+ * Modules/mediastream/MediaDevices.h:
+ * Modules/mediastream/MediaDevices.idl:
+
+ * Modules/mediastream/MediaDevicesEnumerationRequest.cpp:
+ (WebCore::MediaDevicesEnumerationRequest::setDeviceInfo): Remove unnecessary instance variables.
+ * Modules/mediastream/MediaDevicesEnumerationRequest.h:
+
+ * Modules/mediastream/MediaDevicesRequest.cpp:
+ (WebCore::MediaDevicesRequest::start): Remove m_idHashSalt, it isn't used. RealtimeMediaSourceCenter
+ now has the method to hash ids and group IDs.
+ (WebCore::hashString): Deleted.
+ (WebCore::MediaDevicesRequest::hashID): Deleted.
+ * Modules/mediastream/MediaDevicesRequest.h:
+
+ * dom/Document.h:
+ (WebCore::Document::setDeviceIDHashSalt):
+ (WebCore::Document::deviceIDHashSalt):
+
+ * dom/EventNames.h: Add devicechange.
+
+ * dom/EventTargetFactory.in: Add MediaDevices.
+
+ * html/HTMLAttributeNames.in: Add ondevicechange.
+
+ * platform/mediastream/CaptureDeviceManager.cpp:
+ (WebCore::CaptureDeviceManager::captureDeviceFromPersistentID): Renamed from captureDeviceFromDeviceID
+ and changed to return a std::optional<CaptureDevice>.
+ (WebCore::CaptureDeviceManager::captureDeviceFromDeviceID): Deleted.
+
+ * platform/mediastream/RealtimeMediaSourceCenter.cpp:
+ (WebCore::addStringToSHA): New, add string bytes to SHA1. Moved from MediaDevicesRequest
+ so we can use for in testing.
+ (WebCore::RealtimeMediaSourceCenter::hashStringWithSalt): Generate hash for a string with a
+ salt. Moved from MediaDevicesRequest so we can use for in testing.
+ (WebCore::RealtimeMediaSourceCenter::captureDeviceWithUniqueID): Find a CaptureDevice given
+ a unique ID and the process hash salt.
+ (WebCore::RealtimeMediaSourceCenter::setDeviceEnabled): Enable/disable a device. Used for
+ layout tests only.
+ (WebCore::RealtimeMediaSourceCenter::addDevicesChangedObserver): Add a devices changed listener.
+ (WebCore::RealtimeMediaSourceCenter::removeDevicesChangedObserver): Remove a listener.
+ (WebCore::RealtimeMediaSourceCenter::captureDevicesChanged): Notify listeners.
+ * platform/mediastream/RealtimeMediaSourceCenter.h:
+
+ * platform/mediastream/mac/AVCaptureDeviceManager.mm:
+ (WebCore::AVCaptureDeviceManager::addDevicesChangedObserver): Update for change to captureDeviceFromDeviceID.
+
+ * platform/mock/MockRealtimeMediaSource.cpp:
+ (WebCore::MockRealtimeMediaSource::audioDevices): All devices are enabled by default.
+ (WebCore::MockRealtimeMediaSource::videoDevices): Ditto.
+
+ * platform/mock/MockRealtimeMediaSourceCenter.cpp:
+ (WebCore::MockRealtimeMediaSourceCenter::createMediaStream): Drive-by cleanup: use the vector
+ of devices instead of making assumptions about the number.
+ (WebCore::MockRealtimeMediaSourceCenter::getMediaStreamDevices): Only include enabled devices.
+ (WebCore::MockRealtimeMediaSourceCenter::setDeviceEnabled): Enable or disable a device.
+ * platform/mock/MockRealtimeMediaSourceCenter.h:
+
+ * testing/Internals.cpp:
+ (WebCore::Internals::setMediaDeviceState): Enable or disable a mock capture device.
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2017-04-28 Alex Christensen <achristensen@webkit.org>
Fix memory corruption issue after r215883.
/*
* Copyright (C) 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#if ENABLE(MEDIA_STREAM)
#include "Document.h"
+#include "Event.h"
+#include "EventNames.h"
#include "MediaConstraintsImpl.h"
#include "MediaDevicesRequest.h"
#include "MediaTrackSupportedConstraints.h"
-#include "RealtimeMediaSourceCenter.h"
#include "UserMediaRequest.h"
+#include <wtf/RandomNumber.h>
namespace WebCore {
inline MediaDevices::MediaDevices(Document& document)
: ContextDestructionObserver(&document)
+ , m_scheduledEventTimer(*this, &MediaDevices::scheduledEventTimerFired)
{
+ m_deviceChangedToken = RealtimeMediaSourceCenter::singleton().addDevicesChangedObserver([this]() {
+
+ // FIXME: We should only dispatch an event if the user has been granted access to the type of
+ // device that was added or removed.
+ if (!m_scheduledEventTimer.isActive())
+ m_scheduledEventTimer.startOneShot(Seconds(randomNumber() / 2));
+ });
+}
+
+MediaDevices::~MediaDevices()
+{
+ if (m_deviceChangedToken)
+ RealtimeMediaSourceCenter::singleton().removeDevicesChangedObserver(m_deviceChangedToken);
}
Ref<MediaDevices> MediaDevices::create(Document& document)
return result;
}
+void MediaDevices::scheduledEventTimerFired()
+{
+ dispatchEvent(Event::create(eventNames().devicechangeEvent, false, false));
+}
+
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
#if ENABLE(MEDIA_STREAM)
+#include "EventTarget.h"
#include "ExceptionOr.h"
#include "JSDOMPromise.h"
#include "MediaTrackConstraints.h"
+#include "RealtimeMediaSourceCenter.h"
+#include "Timer.h"
namespace WebCore {
struct MediaTrackSupportedConstraints;
-class MediaDevices : public ScriptWrappable, public RefCounted<MediaDevices>, public ContextDestructionObserver {
+class MediaDevices : public RefCounted<MediaDevices>, public ContextDestructionObserver, public EventTargetWithInlineData {
public:
static Ref<MediaDevices> create(Document&);
+ ~MediaDevices();
+
Document* document() const;
using Promise = DOMPromise<IDLInterface<MediaStream>>;
void enumerateDevices(EnumerateDevicesPromise&&) const;
MediaTrackSupportedConstraints getSupportedConstraints();
+ using RefCounted<MediaDevices>::ref;
+ using RefCounted<MediaDevices>::deref;
+
private:
explicit MediaDevices(Document&);
+
+ void scheduledEventTimerFired();
+
+ // EventTargetWithInlineData.
+ EventTargetInterface eventTargetInterface() const override { return MediaDevicesEventTargetInterfaceType; }
+ ScriptExecutionContext* scriptExecutionContext() const final { return m_scriptExecutionContext; }
+ void refEventTarget() override { ref(); }
+ void derefEventTarget() override { deref(); }
+
+ Timer m_scheduledEventTimer;
+ RealtimeMediaSourceCenter::DevicesChangedObserverToken m_deviceChangedToken { 0 };
};
} // namespace WebCore
Conditional=MEDIA_STREAM,
NoInterfaceObject,
] interface MediaDevices {
- // FIXME 169872: missing ondevicechange
+ attribute EventHandler ondevicechange;
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
MediaTrackSupportedConstraints getSupportedConstraints();
void MediaDevicesEnumerationRequest::setDeviceInfo(const Vector<CaptureDevice>& deviceList, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)
{
- m_deviceList = deviceList;
- m_deviceIdentifierHashSalt = deviceIdentifierHashSalt;
- m_originHasPersistentAccess = originHasPersistentAccess;
-
if (m_completionHandler)
- m_completionHandler(m_deviceList, m_deviceIdentifierHashSalt, m_originHasPersistentAccess);
+ m_completionHandler(deviceList, deviceIdentifierHashSalt, originHasPersistentAccess);
m_completionHandler = nullptr;
}
void contextDestroyed() final;
CompletionHandler m_completionHandler;
- Vector<CaptureDevice> m_deviceList;
- String m_deviceIdentifierHashSalt;
- bool m_originHasPersistentAccess { false };
};
} // namespace WebCore
#include "Frame.h"
#include "JSMediaDeviceInfo.h"
#include "MediaDevicesEnumerationRequest.h"
+#include "RealtimeMediaSourceCenter.h"
#include "SecurityOrigin.h"
#include "UserMediaController.h"
#include <wtf/MainThread.h>
return;
Document& document = downcast<Document>(*scriptExecutionContext());
- UserMediaController* controller = UserMediaController::from(document.page());
- if (!controller)
- return;
-
- m_idHashSalt = deviceIdentifierHashSalt;
+ document.setDeviceIDHashSalt(deviceIdentifierHashSalt);
Vector<RefPtr<MediaDeviceInfo>> devices;
for (auto& deviceInfo : captureDevices) {
if (originHasPersistentAccess || document.hasHadActiveMediaStreamTrack())
label = deviceInfo.label();
- auto id = hashID(deviceInfo.persistentId());
+ auto id = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(deviceInfo.persistentId(), deviceIdentifierHashSalt);
if (id.isEmpty())
continue;
- auto groupId = hashID(deviceInfo.groupId());
+ auto groupId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(deviceInfo.groupId(), deviceIdentifierHashSalt);
auto deviceType = deviceInfo.type() == CaptureDevice::DeviceType::Audio ? MediaDeviceInfo::Kind::Audioinput : MediaDeviceInfo::Kind::Videoinput;
devices.append(MediaDeviceInfo::create(scriptExecutionContext(), label, id, groupId, deviceType));
}
m_enumerationRequest->start();
}
-static void hashString(SHA1& sha1, const String& string)
-{
- if (string.isEmpty())
- return;
-
- if (string.is8Bit() && string.containsOnlyASCII()) {
- const uint8_t nullByte = 0;
- sha1.addBytes(string.characters8(), string.length());
- sha1.addBytes(&nullByte, 1);
- return;
- }
-
- auto utf8 = string.utf8();
- sha1.addBytes(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length() + 1); // Include terminating null byte.
-}
-
-String MediaDevicesRequest::hashID(const String& id)
-{
- if (id.isEmpty() || m_idHashSalt.isEmpty())
- return emptyString();
-
- SHA1 sha1;
-
- hashString(sha1, id);
- hashString(sha1, m_idHashSalt);
-
- SHA1::Digest digest;
- sha1.computeHash(digest);
-
- return SHA1::hexDigest(digest).data();
-}
-
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
void contextDestroyed() final;
- String hashID(const String&);
-
MediaDevices::EnumerateDevicesPromise m_promise;
RefPtr<MediaDevicesRequest> m_protector;
RefPtr<MediaDevicesEnumerationRequest> m_enumerationRequest;
-
- String m_idHashSalt;
};
} // namespace WebCore
void enumerateMediaDevices(MediaDevicesEnumerationRequest&);
void cancelMediaDevicesEnumerationRequest(MediaDevicesEnumerationRequest&);
+ void setDeviceIDHashSalt(const String& salt) { m_idHashSalt = salt; }
+ String deviceIDHashSalt() const { return m_idHashSalt; }
+
WEBCORE_EXPORT static const char* supplementName();
static UserMediaController* from(Page* page) { return static_cast<UserMediaController*>(Supplement<Page>::from(page, supplementName())); }
private:
UserMediaClient* m_client;
+ String m_idHashSalt;
};
inline void UserMediaController::requestUserMediaAccess(UserMediaRequest& request)
#if ENABLE(MEDIA_STREAM)
void setHasActiveMediaStreamTrack() { m_hasHadActiveMediaStreamTrack = true; }
bool hasHadActiveMediaStreamTrack() const { return m_hasHadActiveMediaStreamTrack; }
+ void setDeviceIDHashSalt(const String& salt) { m_idHashSalt = salt; }
+ String deviceIDHashSalt() const { return m_idHashSalt; }
#endif
// FIXME: Find a better place for this functionality.
#endif
#if ENABLE(MEDIA_STREAM)
+ String m_idHashSalt;
bool m_hasHadActiveMediaStreamTrack { false };
#endif
macro(cut) \
macro(datachannel) \
macro(dblclick) \
+ macro(devicechange) \
macro(devicemotion) \
macro(deviceorientation) \
macro(dischargingtimechange) \
IDBRequest conditional=INDEXED_DATABASE
IDBTransaction conditional=INDEXED_DATABASE
MediaController conditional=VIDEO
+MediaDevices conditional=MEDIA_STREAM
MediaKeySession conditional=ENCRYPTED_MEDIA
MediaRemoteControls conditional=MEDIA_SESSION
MediaSource conditional=MEDIA_SOURCE
oncopy
oncut
ondblclick
+ondevicechange
ondrag
ondragend
ondragenter
return sourcesInfo;
}
-bool CaptureDeviceManager::captureDeviceFromDeviceID(const String& captureDeviceID, CaptureDevice& foundDevice)
+std::optional<CaptureDevice> CaptureDeviceManager::captureDeviceFromPersistentID(const String& captureDeviceID)
{
for (auto& device : captureDevices()) {
- if (device.persistentId() == captureDeviceID) {
- foundDevice = device;
- return true;
- }
+ if (device.persistentId() == captureDeviceID)
+ return device;
}
- return false;
+ return std::nullopt;
}
+#if 0
+std::optional<CaptureDevice> CaptureDeviceManager::captureDeviceFromPersistentID(const String& captureDeviceID, const String& idHashSalt)
+{
+ for (auto& device : captureDevices()) {
+ auto hashedID = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.persistentId(), idHashSalt);
+ if (device.persistentId() == captureDeviceID)
+ return device;
+ }
+
+ return std::nullopt;
+}
+
+for (auto& captureDevice : getMediaStreamDevices()) {
+ auto hashedID = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(captureDevice.persistentId(), idHashSalt);
+ if (hashedID == uniqueID)
+ return String { captureDevice.persistentId() };
+}
+
+return Exception { NOT_FOUND_ERR };
+
+#endif
+
std::optional<CaptureDevice> CaptureDeviceManager::deviceWithUID(const String& deviceUID, RealtimeMediaSource::Type type)
{
for (auto& captureDevice : captureDevices()) {
protected:
virtual ~CaptureDeviceManager();
- bool captureDeviceFromDeviceID(const String& captureDeviceID, CaptureDevice& source);
+ std::optional<CaptureDevice> captureDeviceFromPersistentID(const String& captureDeviceID);
};
} // namespace WebCore
*/
#include "config.h"
+#include "RealtimeMediaSourceCenter.h"
#if ENABLE(MEDIA_STREAM)
-#include "RealtimeMediaSourceCenter.h"
#include "MediaStreamPrivate.h"
+#include <wtf/SHA1.h>
namespace WebCore {
if (m_videoCaptureDeviceManager == &deviceManager)
m_videoCaptureDeviceManager = nullptr;
}
+
+static void addStringToSHA1(SHA1& sha1, const String& string)
+{
+ if (string.isEmpty())
+ return;
+
+ if (string.is8Bit() && string.containsOnlyASCII()) {
+ const uint8_t nullByte = 0;
+ sha1.addBytes(string.characters8(), string.length());
+ sha1.addBytes(&nullByte, 1);
+ return;
+ }
+
+ auto utf8 = string.utf8();
+ sha1.addBytes(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length() + 1); // Include terminating null byte.
+}
+
+String RealtimeMediaSourceCenter::hashStringWithSalt(const String& id, const String& hashSalt)
+{
+ if (id.isEmpty() || hashSalt.isEmpty())
+ return emptyString();
+
+ SHA1 sha1;
+
+ addStringToSHA1(sha1, id);
+ addStringToSHA1(sha1, hashSalt);
+ SHA1::Digest digest;
+ sha1.computeHash(digest);
+
+ return SHA1::hexDigest(digest).data();
+}
+
+std::optional<CaptureDevice> RealtimeMediaSourceCenter::captureDeviceWithUniqueID(const String& uniqueID, const String& idHashSalt)
+{
+ for (auto& device : getMediaStreamDevices()) {
+ if (uniqueID == hashStringWithSalt(device.persistentId(), idHashSalt))
+ return device;
+ }
+
+ return std::nullopt;
+}
+
+ExceptionOr<void> RealtimeMediaSourceCenter::setDeviceEnabled(const String&, bool)
+{
+ return Exception { NOT_FOUND_ERR, ASCIILiteral("Not implemented!") };
+}
+
+RealtimeMediaSourceCenter::DevicesChangedObserverToken RealtimeMediaSourceCenter::addDevicesChangedObserver(std::function<void()>&& observer)
+{
+ DevicesChangedObserverToken nextToken = 0;
+ m_devicesChangedObservers.set(++nextToken, WTFMove(observer));
+ return nextToken;
+}
+
+void RealtimeMediaSourceCenter::removeDevicesChangedObserver(DevicesChangedObserverToken token)
+{
+ bool wasRemoved = m_devicesChangedObservers.remove(token);
+ ASSERT_UNUSED(wasRemoved, wasRemoved);
+}
+
+void RealtimeMediaSourceCenter::captureDevicesChanged()
+{
+ auto callbacks = m_devicesChangedObservers;
+ for (auto it : callbacks)
+ it.value();
+}
+
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RealtimeMediaSourceCenter_h
-#define RealtimeMediaSourceCenter_h
+#pragma once
#if ENABLE(MEDIA_STREAM)
+#include "ExceptionOr.h"
#include "RealtimeMediaSource.h"
#include "RealtimeMediaSourceSupportedConstraints.h"
#include <wtf/PassRefPtr.h>
WEBCORE_EXPORT void unsetVideoCaptureDeviceManager(CaptureDeviceManager&);
CaptureDeviceManager* videoCaptureDeviceManager() const { return m_videoCaptureDeviceManager; }
+ String hashStringWithSalt(const String& id, const String& hashSalt);
+ WEBCORE_EXPORT std::optional<CaptureDevice> captureDeviceWithUniqueID(const String& id, const String& hashSalt);
+ virtual ExceptionOr<void> setDeviceEnabled(const String&, bool);
+
+ using DevicesChangedObserverToken = unsigned;
+ DevicesChangedObserverToken addDevicesChangedObserver(std::function<void()>&&);
+ void removeDevicesChangedObserver(DevicesChangedObserverToken);
+ void captureDevicesChanged();
+
protected:
RealtimeMediaSourceCenter();
CaptureDeviceManager* m_audioCaptureDeviceManager { nullptr };
CaptureDeviceManager* m_videoCaptureDeviceManager { nullptr };
+
+ HashMap<unsigned, std::function<void()>> m_devicesChangedObservers;
};
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)
-#endif // RealtimeMediaSourceCenter_h
if (!hasMatchingType)
continue;
- CaptureDevice existingCaptureDevice;
- if (captureDeviceFromDeviceID(platformDevice.uniqueID, existingCaptureDevice) && existingCaptureDevice.type() == type)
+ std::optional<CaptureDevice> existingCaptureDevice = captureDeviceFromPersistentID(platformDevice.uniqueID);
+ if (existingCaptureDevice && existingCaptureDevice->type() == type)
continue;
CaptureDevice captureDevice(platformDevice.uniqueID, type, platformDevice.localizedName);
{
static NeverDestroyed<Vector<CaptureDevice>> info;
if (!info.get().size()) {
- info.get().append(CaptureDevice("239c24b0-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Audio, "Mock audio device 1"));
- info.get().append(CaptureDevice("239c24b1-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Audio, "Mock audio device 2"));
+ auto captureDevice = CaptureDevice("239c24b0-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Audio, "Mock audio device 1");
+ captureDevice.setEnabled(true);
+ info.get().append(captureDevice);
+
+ captureDevice = CaptureDevice("239c24b1-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Audio, "Mock audio device 2");
+ captureDevice.setEnabled(true);
+ info.get().append(captureDevice);
}
return info;
}
{
static NeverDestroyed<Vector<CaptureDevice>> info;
if (!info.get().size()) {
- info.get().append(CaptureDevice("239c24b2-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Video, "Mock video device 1"));
- info.get().append(CaptureDevice("239c24b3-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Video, "Mock video device 2"));
+ auto captureDevice = CaptureDevice("239c24b2-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Video, "Mock video device 1");
+ captureDevice.setEnabled(true);
+ info.get().append(captureDevice);
+
+ captureDevice = CaptureDevice("239c24b3-2b15-11e3-8224-0800200c9a66", CaptureDevice::DeviceType::Video, "Mock video device 2");
+ captureDevice.setEnabled(true);
+ info.get().append(captureDevice);
}
return info;
}
Vector<Ref<RealtimeMediaSource>> videoSources;
if (!audioDeviceID.isEmpty()) {
- auto& audioDevices = MockRealtimeMediaSource::audioDevices();
- if (audioDeviceID == audioDevices[0].persistentId()) {
- auto source = MockRealtimeAudioSource::create(audioDevices[0].label(), audioConstraints);
- if (source)
- audioSources.append(source.releaseNonNull());
- } else if (audioDeviceID == audioDevices[1].persistentId()) {
- auto source = MockRealtimeAudioSource::create(audioDevices[1].label(), audioConstraints);
- if (source)
- audioSources.append(source.releaseNonNull());
+ for (auto& captureDevice : MockRealtimeMediaSource::audioDevices()) {
+ if (!captureDevice.enabled())
+ continue;
+
+ if (audioDeviceID == captureDevice.persistentId()) {
+ auto source = MockRealtimeAudioSource::create(captureDevice.label(), audioConstraints);
+ if (source)
+ audioSources.append(source.releaseNonNull());
+ }
}
}
if (!videoDeviceID.isEmpty()) {
- auto& videoDevices = MockRealtimeMediaSource::videoDevices();
- if (videoDeviceID == videoDevices[0].persistentId()) {
- auto source = MockRealtimeVideoSource::create(videoDevices[0].label(), videoConstraints);
- if (source)
- videoSources.append(source.releaseNonNull());
- } else if (videoDeviceID == videoDevices[1].persistentId()) {
- auto source = MockRealtimeVideoSource::create(videoDevices[1].label(), videoConstraints);
- if (source)
- videoSources.append(source.releaseNonNull());
+ for (auto& captureDevice : MockRealtimeMediaSource::videoDevices()) {
+ if (!captureDevice.enabled())
+ continue;
+
+ if (videoDeviceID == captureDevice.persistentId()) {
+ auto source = MockRealtimeVideoSource::create(captureDevice.label(), videoConstraints);
+ if (source)
+ videoSources.append(source.releaseNonNull());
+ }
}
}
{
Vector<CaptureDevice> sources;
- sources.appendVector(MockRealtimeMediaSource::audioDevices());
- sources.appendVector(MockRealtimeMediaSource::videoDevices());
+ for (auto& captureDevice : MockRealtimeMediaSource::audioDevices()) {
+ if (!captureDevice.enabled())
+ continue;
+
+ sources.append(captureDevice);
+ }
+
+ for (auto& captureDevice : MockRealtimeMediaSource::videoDevices()) {
+ if (!captureDevice.enabled())
+ continue;
+
+ sources.append(captureDevice);
+ }
return sources;
}
return &MockRealtimeVideoSource::factory();
}
+ExceptionOr<void> MockRealtimeMediaSourceCenter::setDeviceEnabled(const String& id, bool enabled)
+{
+ for (auto& captureDevice : getMediaStreamDevices()) {
+ if (id == captureDevice.persistentId()) {
+ if (enabled != captureDevice.enabled()) {
+ captureDevice.setEnabled(enabled);
+ captureDevicesChanged();
+ }
+
+ return { };
+ }
+ }
+
+ return Exception { NOT_FOUND_ERR };
+}
} // namespace WebCore
RealtimeMediaSource::CaptureFactory* defaultAudioFactory() final;
RealtimeMediaSource::CaptureFactory* defaultVideoFactory() final;
+
+ ExceptionOr<void> setDeviceEnabled(const String& persistentID, bool) final;
};
}
m_nextTrackFramePromise = std::nullopt;
}
+ExceptionOr<void> Internals::setMediaDeviceState(const String& id, const String& property, bool value)
+{
+ auto* document = contextDocument();
+ if (!document)
+ return Exception { INVALID_ACCESS_ERR, ASCIILiteral("No context document") };
+
+ if (!equalLettersIgnoringASCIICase(property, "enabled"))
+ return Exception { INVALID_ACCESS_ERR, makeString("\"" + property, "\" is not a valid property for this method.") };
+
+ auto salt = document->deviceIDHashSalt();
+ std::optional<CaptureDevice> device = RealtimeMediaSourceCenter::singleton().captureDeviceWithUniqueID(id, salt);
+ if (!device)
+ return Exception { INVALID_ACCESS_ERR, makeString("device with ID \"" + id, "\" not found.") };
+
+ auto result = RealtimeMediaSourceCenter::singleton().setDeviceEnabled(device->persistentId(), value);
+ if (result.hasException())
+ return result.releaseException();
+
+ return { };
+}
+
#endif
} // namespace WebCore
#endif
#if ENABLE(MEDIA_STREAM)
+ ExceptionOr<void> setMediaDeviceState(const String& id, const String& property, bool value);
unsigned long trackAudioSampleCount() const { return m_trackAudioSampleCount; }
unsigned long trackVideoSampleCount() const { return m_trackVideoSampleCount; }
void observeMediaStreamTrack(MediaStreamTrack&);
[Conditional=MEDIA_STREAM] Promise<ImageData> grabNextMediaStreamTrackFrame();
[Conditional=MEDIA_STREAM] readonly attribute unsigned long trackAudioSampleCount;
[Conditional=MEDIA_STREAM] readonly attribute unsigned long trackVideoSampleCount;
+ [Conditional=MEDIA_STREAM, MayThrowException] void setMediaDeviceState(DOMString deviceID, DOMString property, boolean value);
};