blob: 5c834e34d22e65484c383bc31fd22717f9f504f5 [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
* Copyright (C) 2013-2022 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "MockRealtimeMediaSourceCenter.h"
#if ENABLE(MEDIA_STREAM)
#include "CaptureDevice.h"
#include "Logging.h"
#include "MediaConstraints.h"
#include "MockRealtimeAudioSource.h"
#include "MockRealtimeVideoSource.h"
#include "NotImplemented.h"
#include "RealtimeMediaSourceSettings.h"
#include <math.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/text/StringView.h>
#if PLATFORM(COCOA)
#include "CoreAudioCaptureSource.h"
#include "DisplayCaptureSourceCocoa.h"
#include "MockRealtimeVideoSourceMac.h"
#endif
#if USE(GSTREAMER)
#include "MockRealtimeVideoSourceGStreamer.h"
#endif
namespace WebCore {
static inline Vector<MockMediaDevice> defaultDevices()
{
return Vector<MockMediaDevice> {
MockMediaDevice { "239c24b0-2b15-11e3-8224-0800200c9a66"_s, "Mock audio device 1"_s, MockMicrophoneProperties { 44100 } },
MockMediaDevice { "239c24b1-2b15-11e3-8224-0800200c9a66"_s, "Mock audio device 2"_s, MockMicrophoneProperties { 48000 } },
MockMediaDevice { "239c24b0-2b15-11e3-8224-0800200c9a67"_s, "Mock speaker device 1"_s, MockSpeakerProperties { "239c24b0-2b15-11e3-8224-0800200c9a66"_s, 44100 } },
MockMediaDevice { "239c24b1-2b15-11e3-8224-0800200c9a67"_s, "Mock speaker device 2"_s, MockSpeakerProperties { "239c24b1-2b15-11e3-8224-0800200c9a66"_s, 48000 } },
MockMediaDevice { "239c24b2-2b15-11e3-8224-0800200c9a67"_s, "Mock speaker device 3"_s, MockSpeakerProperties { String { }, 48000 } },
MockMediaDevice { "239c24b2-2b15-11e3-8224-0800200c9a66"_s, "Mock video device 1"_s,
MockCameraProperties {
30,
RealtimeMediaSourceSettings::VideoFacingMode::User, {
{ { 2560, 1440 }, { { 10, 10 }, { 7.5, 7.5 }, { 5, 5 } } },
{ { 1280, 720 }, { { 30, 30}, { 27.5, 27.5}, { 25, 25}, { 22.5, 22.5}, { 20, 20}, { 17.5, 17.5}, { 15, 15}, { 12.5, 12.5}, { 10, 10}, { 7.5, 7.5}, { 5, 5} } },
{ { 640, 480 }, { { 30, 30}, { 27.5, 27.5}, { 25, 25}, { 22.5, 22.5}, { 20, 20}, { 17.5, 17.5}, { 15, 15}, { 12.5, 12.5}, { 10, 10}, { 7.5, 7.5}, { 5, 5} } },
{ { 112, 112 }, { { 30, 30}, { 27.5, 27.5}, { 25, 25}, { 22.5, 22.5}, { 20, 20}, { 17.5, 17.5}, { 15, 15}, { 12.5, 12.5}, { 10, 10}, { 7.5, 7.5}, { 5, 5} } },
},
Color::black,
} },
MockMediaDevice { "239c24b3-2b15-11e3-8224-0800200c9a66"_s, "Mock video device 2"_s,
MockCameraProperties {
15,
RealtimeMediaSourceSettings::VideoFacingMode::Environment, {
{ { 3840, 2160 }, { { 2, 30 } } },
{ { 1920, 1080 }, { { 2, 30 } } },
{ { 1280, 720 }, { { 3, 120 } } },
{ { 960, 540 }, { { 3, 60 } } },
{ { 640, 480 }, { { 2, 30 } } },
{ { 352, 288 }, { { 2, 30 } } },
{ { 320, 240 }, { { 2, 30 } } },
{ { 160, 120 }, { { 2, 30 } } },
},
Color::darkGray,
} },
MockMediaDevice { "SCREEN-1"_s, "Mock screen device 1"_s, MockDisplayProperties { CaptureDevice::DeviceType::Screen, Color::lightGray, { 1920, 1080 } } },
MockMediaDevice { "SCREEN-2"_s, "Mock screen device 2"_s, MockDisplayProperties { CaptureDevice::DeviceType::Screen, Color::yellow, { 3840, 2160 } } },
MockMediaDevice { "WINDOW-1"_s, "Mock window device 1"_s, MockDisplayProperties { CaptureDevice::DeviceType::Window, SRGBA<uint8_t> { 255, 241, 181 }, { 640, 480 } } },
MockMediaDevice { "WINDOW-2"_s, "Mock window device 2"_s, MockDisplayProperties { CaptureDevice::DeviceType::Window, SRGBA<uint8_t> { 255, 208, 181 }, { 1280, 600 } } },
};
}
class MockRealtimeVideoSourceFactory : public VideoCaptureFactory {
public:
CaptureSourceOrError createVideoCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
{
ASSERT(device.type() == CaptureDevice::DeviceType::Camera);
if (!MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType::Camera, device.persistentId()))
return { "Unable to find mock camera device with given persistentID"_s };
return MockRealtimeVideoSource::create(String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt), constraints);
}
private:
CaptureDeviceManager& videoCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().videoCaptureDeviceManager(); }
};
#if PLATFORM(MAC)
class MockDisplayCapturer final : public DisplayCaptureSourceCocoa::Capturer {
public:
explicit MockDisplayCapturer(const CaptureDevice&);
private:
bool start() final;
void stop() final { m_source->stop(); }
DisplayCaptureSourceCocoa::DisplayFrameType generateFrame() final;
RealtimeMediaSourceSettings::DisplaySurfaceType surfaceType() const final { return RealtimeMediaSourceSettings::DisplaySurfaceType::Monitor; }
void commitConfiguration(const RealtimeMediaSourceSettings&) final;
CaptureDevice::DeviceType deviceType() const final { return CaptureDevice::DeviceType::Screen; }
IntSize intrinsicSize() const final;
#if !RELEASE_LOG_DISABLED
const char* logClassName() const final { return "MockDisplayCapturer"; }
#endif
Ref<MockRealtimeVideoSource> m_source;
RealtimeMediaSourceSettings m_settings;
};
MockDisplayCapturer::MockDisplayCapturer(const CaptureDevice& device)
: m_source(MockRealtimeVideoSourceMac::createForMockDisplayCapturer(String { device.persistentId() }, String { device.label() }, String { }))
{
}
bool MockDisplayCapturer::start()
{
ASSERT(m_settings.frameRate());
m_source->start();
return true;
}
void MockDisplayCapturer::commitConfiguration(const RealtimeMediaSourceSettings& settings)
{
// FIXME: Update m_source width, height and frameRate according settings
m_settings = settings;
}
DisplayCaptureSourceCocoa::DisplayFrameType MockDisplayCapturer::generateFrame()
{
if (auto* imageBuffer = m_source->imageBuffer())
return imageBuffer->copyNativeImage();
return { };
}
IntSize MockDisplayCapturer::intrinsicSize() const
{
auto device = MockRealtimeMediaSourceCenter::mockDeviceWithPersistentID(m_source->persistentID());
ASSERT(device);
if (!device)
return { };
ASSERT(device->isDisplay());
if (!device->isDisplay())
return { };
auto& properties = std::get<MockDisplayProperties>(device->properties);
return properties.defaultSize;
}
#endif // PLATFORM(MAC)
class MockRealtimeDisplaySourceFactory : public DisplayCaptureFactory {
public:
CaptureSourceOrError createDisplayCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
{
if (!MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(device.type(), device.persistentId()))
return { "Unable to find mock display device with given persistentID"_s };
switch (device.type()) {
case CaptureDevice::DeviceType::Screen:
case CaptureDevice::DeviceType::Window:
#if PLATFORM(MAC)
return DisplayCaptureSourceCocoa::create(UniqueRef<DisplayCaptureSourceCocoa::Capturer>(makeUniqueRef<MockDisplayCapturer>(device)), device, WTFMove(hashSalt), constraints);
#elif USE(GSTREAMER)
return MockDisplayCaptureSourceGStreamer::create(device, WTFMove(hashSalt), constraints);
#else
return MockRealtimeVideoSource::create(String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt), constraints);
#endif
break;
case CaptureDevice::DeviceType::Microphone:
case CaptureDevice::DeviceType::Speaker:
case CaptureDevice::DeviceType::Camera:
case CaptureDevice::DeviceType::Unknown:
ASSERT_NOT_REACHED();
break;
}
return { };
}
private:
DisplayCaptureManager& displayCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().displayCaptureDeviceManager(); }
};
class MockRealtimeAudioSourceFactory final : public AudioCaptureFactory {
public:
CaptureSourceOrError createAudioCaptureSource(const CaptureDevice& device, String&& hashSalt, const MediaConstraints* constraints) final
{
ASSERT(device.type() == CaptureDevice::DeviceType::Microphone);
if (!MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType::Microphone, device.persistentId()))
return { "Unable to find mock microphone device with given persistentID"_s };
return MockRealtimeAudioSource::create(String { device.persistentId() }, String { device.label() }, WTFMove(hashSalt), constraints);
}
private:
#if PLATFORM(IOS_FAMILY)
void setActiveSource(RealtimeMediaSource& source) final { CoreAudioCaptureSourceFactory::singleton().setActiveSource(source); }
void unsetActiveSource(RealtimeMediaSource& source) final { CoreAudioCaptureSourceFactory::singleton().unsetActiveSource(source); }
RealtimeMediaSource* activeSource() final { return CoreAudioCaptureSourceFactory::singleton().activeSource(); }
#endif
CaptureDeviceManager& audioCaptureDeviceManager() final { return MockRealtimeMediaSourceCenter::singleton().audioCaptureDeviceManager(); }
const Vector<CaptureDevice>& speakerDevices() const final { return MockRealtimeMediaSourceCenter::speakerDevices(); }
};
static Vector<MockMediaDevice>& devices()
{
static NeverDestroyed devices = defaultDevices();
return devices;
}
static HashMap<String, MockMediaDevice>& deviceMap()
{
static NeverDestroyed map = [] {
HashMap<String, MockMediaDevice> map;
for (auto& device : devices())
map.add(device.persistentId, device);
return map;
}();
return map;
}
static inline Vector<CaptureDevice>& deviceListForDevice(const MockMediaDevice& device)
{
if (device.isMicrophone())
return MockRealtimeMediaSourceCenter::microphoneDevices();
if (device.isSpeaker())
return MockRealtimeMediaSourceCenter::speakerDevices();
if (device.isCamera())
return MockRealtimeMediaSourceCenter::videoDevices();
ASSERT(device.isDisplay());
return MockRealtimeMediaSourceCenter::displayDevices();
}
MockRealtimeMediaSourceCenter& MockRealtimeMediaSourceCenter::singleton()
{
static NeverDestroyed<MockRealtimeMediaSourceCenter> center;
return center;
}
void MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(bool enabled)
{
MockRealtimeMediaSourceCenter& mock = singleton();
if (mock.m_isEnabled == enabled)
return;
mock.m_isEnabled = enabled;
RealtimeMediaSourceCenter& center = RealtimeMediaSourceCenter::singleton();
if (mock.m_isEnabled) {
if (mock.m_isMockAudioCaptureEnabled)
center.setAudioCaptureFactory(mock.audioCaptureFactory());
if (mock.m_isMockVideoCaptureEnabled)
center.setVideoCaptureFactory(mock.videoCaptureFactory());
if (mock.m_isMockDisplayCaptureEnabled)
center.setDisplayCaptureFactory(mock.displayCaptureFactory());
return;
}
if (mock.m_isMockAudioCaptureEnabled)
center.unsetAudioCaptureFactory(mock.audioCaptureFactory());
if (mock.m_isMockVideoCaptureEnabled)
center.unsetVideoCaptureFactory(mock.videoCaptureFactory());
if (mock.m_isMockDisplayCaptureEnabled)
center.unsetDisplayCaptureFactory(mock.displayCaptureFactory());
}
bool MockRealtimeMediaSourceCenter::mockRealtimeMediaSourceCenterEnabled()
{
return singleton().m_isEnabled;
}
static CaptureDevice toCaptureDevice(const MockMediaDevice& device)
{
auto captureDevice = device.captureDevice();
captureDevice.setEnabled(true);
captureDevice.setIsMockDevice(true);
return captureDevice;
}
static void createMockDevice(const MockMediaDevice& device)
{
deviceListForDevice(device).append(toCaptureDevice(device));
}
void MockRealtimeMediaSourceCenter::resetDevices()
{
setDevices(defaultDevices());
RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
}
void MockRealtimeMediaSourceCenter::setMockCameraIsInterrupted(bool isInterrupted)
{
MockRealtimeVideoSource::setIsInterrupted(isInterrupted);
}
void MockRealtimeMediaSourceCenter::setDevices(Vector<MockMediaDevice>&& newMockDevices)
{
microphoneDevices().clear();
speakerDevices().clear();
videoDevices().clear();
displayDevices().clear();
auto& mockDevices = devices();
mockDevices = WTFMove(newMockDevices);
auto& map = deviceMap();
map.clear();
for (const auto& device : mockDevices) {
map.add(device.persistentId, device);
createMockDevice(device);
}
RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
}
void MockRealtimeMediaSourceCenter::addDevice(const MockMediaDevice& device)
{
devices().append(device);
deviceMap().set(device.persistentId, device);
createMockDevice(device);
RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
}
void MockRealtimeMediaSourceCenter::removeDevice(const String& persistentId)
{
auto& map = deviceMap();
auto iterator = map.find(persistentId);
if (iterator == map.end())
return;
devices().removeFirstMatching([&persistentId](const auto& device) {
return device.persistentId == persistentId;
});
deviceListForDevice(iterator->value).removeFirstMatching([&persistentId](const auto& device) {
return device.persistentId() == persistentId;
});
map.remove(iterator);
RealtimeMediaSourceCenter::singleton().captureDevicesChanged();
}
std::optional<MockMediaDevice> MockRealtimeMediaSourceCenter::mockDeviceWithPersistentID(const String& id)
{
ASSERT(!id.isEmpty());
auto& map = deviceMap();
auto iterator = map.find(id);
if (iterator == map.end())
return std::nullopt;
return iterator->value;
}
std::optional<CaptureDevice> MockRealtimeMediaSourceCenter::captureDeviceWithPersistentID(CaptureDevice::DeviceType type, const String& id)
{
ASSERT(!id.isEmpty());
auto& map = deviceMap();
auto iterator = map.find(id);
if (iterator == map.end() || iterator->value.type() != type)
return std::nullopt;
return toCaptureDevice(iterator->value);
}
Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::microphoneDevices()
{
static NeverDestroyed microphoneDevices = [] {
Vector<CaptureDevice> microphoneDevices;
for (const auto& device : devices()) {
if (device.isMicrophone())
microphoneDevices.append(toCaptureDevice(device));
}
return microphoneDevices;
}();
return microphoneDevices;
}
Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::speakerDevices()
{
static NeverDestroyed speakerDevices = [] {
Vector<CaptureDevice> speakerDevices;
for (const auto& device : devices()) {
if (device.isSpeaker())
speakerDevices.append(toCaptureDevice(device));
}
return speakerDevices;
}();
return speakerDevices;
}
Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::videoDevices()
{
static NeverDestroyed videoDevices = [] {
Vector<CaptureDevice> videoDevices;
for (const auto& device : devices()) {
if (device.isCamera())
videoDevices.append(toCaptureDevice(device));
}
return videoDevices;
}();
return videoDevices;
}
Vector<CaptureDevice>& MockRealtimeMediaSourceCenter::displayDevices()
{
static NeverDestroyed displayDevices = [] {
Vector<CaptureDevice> displayDevices;
for (const auto& device : devices()) {
if (device.isDisplay())
displayDevices.append(device.captureDevice());
}
return displayDevices;
}();
return displayDevices;
}
AudioCaptureFactory& MockRealtimeMediaSourceCenter::audioCaptureFactory()
{
static NeverDestroyed<MockRealtimeAudioSourceFactory> factory;
return factory.get();
}
VideoCaptureFactory& MockRealtimeMediaSourceCenter::videoCaptureFactory()
{
static NeverDestroyed<MockRealtimeVideoSourceFactory> factory;
return factory.get();
}
DisplayCaptureFactory& MockRealtimeMediaSourceCenter::displayCaptureFactory()
{
static NeverDestroyed<MockRealtimeDisplaySourceFactory> factory;
return factory.get();
}
void MockRealtimeMediaSourceCenter::MockDisplayCaptureDeviceManager::windowDevices(Vector<DisplayCaptureManager::WindowCaptureDevice>& windowDevices)
{
auto devices = MockRealtimeMediaSourceCenter::displayDevices();
for (auto device : devices) {
if (!device.enabled() || device.type() != CaptureDevice::DeviceType::Window)
continue;
windowDevices.append({ WTFMove(device), "Mock Application"_s });
}
}
} // namespace WebCore
#endif // ENABLE(MEDIA_STREAM)