Skip to content

Commit

Permalink
Do not take into account device orientation for cameras whose positio…
Browse files Browse the repository at this point in the history
…n is unspecified

https://bugs.webkit.org/show_bug.cgi?id=255448
rdar://problem/108043783

Reviewed by Eric Carlson.

Skip orientation monitoring in case facing mode is unspecified, as this means the sensor is not guaranteed to be attached to the device.
Update mock infrastructure accordingly and allow to add cameras with undefined facing mode.

Covered by added test.

* LayoutTests/fast/mediastream/camera-unknown-facing-mode-expected.txt: Added.
* LayoutTests/fast/mediastream/camera-unknown-facing-mode.html: Added.
* Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm:
(WebCore::AVVideoCaptureSource::monitorOrientation):
* Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp:
(WebCore::MockRealtimeVideoSource::capabilities):
(WebCore::MockRealtimeVideoSource::settings):
(WebCore::MockRealtimeVideoSource::monitorOrientation):
* Source/WebKit/UIProcess/API/C/WKMockMediaDevice.cpp:
(WKAddMockMediaDevice):
* Source/WebKit/UIProcess/API/C/WKMockMediaDevice.h:
* Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl:
* Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp:
(WTR::TestRunner::addMockMediaDevice):
(WTR::TestRunner::addMockCameraDevice):
(WTR::TestRunner::addMockMicrophoneDevice):
(WTR::TestRunner::addMockScreenDevice):
* Tools/WebKitTestRunner/InjectedBundle/TestRunner.h:
* Tools/WebKitTestRunner/TestController.cpp:
(WTR::TestController::addMockMediaDevice):
* Tools/WebKitTestRunner/TestController.h:
* Tools/WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::didReceiveSynchronousMessageFromInjectedBundle):

Canonical link: https://commits.webkit.org/263018@main
  • Loading branch information
youennf committed Apr 17, 2023
1 parent 79e6530 commit d0a00be
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 24 deletions.
@@ -0,0 +1,5 @@


PASS Width and height before and after rotation with default device
PASS Width and height before and after rotation with new device

79 changes: 79 additions & 0 deletions LayoutTests/fast/mediastream/camera-unknown-facing-mode.html
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<video id="video"></video>
<script>
let setup = async (test) => {
if (!window.testRunner)
return Promise.reject("test requires internal API");

test.add_cleanup(() => {
testRunner.resetMockMediaDevices();
testRunner.setMockCameraOrientation(0);
});
}

async function getSettingsBeforeAndAfterRotation(deviceId)
{
testRunner.setMockCameraOrientation(0);

const stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId } });
video.srcObject = stream;
await video.play();

const track = stream.getVideoTracks()[0];
const initialSettings = track.getSettings();

video.srcObject = null;

testRunner.setMockCameraOrientation(90);
await new Promise(resolve => setTimeout(resolve, 100));

video.srcObject = stream;
await video.play();

return [initialSettings, track.getSettings()];
}

promise_test(async (test) => {
await setup(test);

const results = await getSettingsBeforeAndAfterRotation("default");

assert_equals(results[0].facingMode, "user");
assert_equals(results[0].width, 640, "initial width");
assert_equals(results[0].height, 480, "initial height");
assert_equals(results[1].width, 480, "final width");
assert_equals(results[1].height, 640, "final height");
}, "Width and height before and after rotation with default device");

promise_test(async (test) => {
await setup(test);

testRunner.addMockCameraDevice("myCamera", "my new camera", { facingMode: "unknown", fillColor: "green" });
const stream = await navigator.mediaDevices.getUserMedia({ video: true });

const devices = await navigator.mediaDevices.enumerateDevices();
let deviceId = "";
devices.forEach(device => {
if (device.label == "my new camera")
deviceId = device.deviceId;
});
stream.getVideoTracks()[0].stop();

const results = await getSettingsBeforeAndAfterRotation(deviceId);

assert_equals(results[0].facingMode, undefined);
assert_equals(results[0].width, 640, "initial width");
assert_equals(results[0].height, 480, "initial height");
assert_equals(results[1].width, 640, "final width");
assert_equals(results[1].height, 480, "final height");
}, "Width and height before and after rotation with new device");
</script>
</body>
</html>
Expand Up @@ -615,6 +615,9 @@ static inline IntDegrees sensorOrientationFromVideoOutput(AVCaptureVideoDataOutp
void AVVideoCaptureSource::monitorOrientation(OrientationNotifier& notifier)
{
#if PLATFORM(IOS_FAMILY)
if ([device() deviceType] == AVCaptureDeviceTypeExternalUnknown)
return;

notifier.addObserver(*this);
orientationChanged(notifier.orientation());
#else
Expand Down
8 changes: 5 additions & 3 deletions Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp
Expand Up @@ -143,7 +143,8 @@ const RealtimeMediaSourceCapabilities& MockRealtimeVideoSource::capabilities()

if (mockCamera()) {
auto facingMode = std::get<MockCameraProperties>(m_device.properties).facingMode;
capabilities.addFacingMode(facingMode);
if (facingMode != VideoFacingMode::Unknown)
capabilities.addFacingMode(facingMode);
capabilities.setDeviceId(hashedId());
updateCapabilities(capabilities);

Expand Down Expand Up @@ -208,7 +209,8 @@ const RealtimeMediaSourceSettings& MockRealtimeVideoSource::settings()
supportedConstraints.setSupportsAspectRatio(true);
supportedConstraints.setSupportsDeviceId(true);
if (mockCamera()) {
supportedConstraints.setSupportsFacingMode(true);
if (facingMode() != VideoFacingMode::Unknown)
supportedConstraints.setSupportsFacingMode(true);
if (isZoomSupported(presets())) {
supportedConstraints.setSupportsZoom(true);
settings.setZoom(zoom());
Expand Down Expand Up @@ -538,7 +540,7 @@ void MockRealtimeVideoSource::orientationChanged(IntDegrees orientation)

void MockRealtimeVideoSource::monitorOrientation(OrientationNotifier& notifier)
{
if (!mockCamera())
if (!mockCamera() || std::get<MockCameraProperties>(m_device.properties).facingMode == VideoFacingMode::Unknown)
return;

notifier.addObserver(*this);
Expand Down
31 changes: 23 additions & 8 deletions Source/WebKit/UIProcess/API/C/WKMockMediaDevice.cpp
Expand Up @@ -28,27 +28,42 @@
#include "WKMockMediaDevice.h"

#include "WKAPICast.h"
#include "WKDictionary.h"
#include "WKRetainPtr.h"
#include "WKString.h"
#include "WebProcessPool.h"
#include <WebCore/MockMediaDevice.h>

using namespace WebKit;

void WKAddMockMediaDevice(WKContextRef context, WKStringRef persistentId, WKStringRef label, WKStringRef type)
void WKAddMockMediaDevice(WKContextRef context, WKStringRef persistentId, WKStringRef label, WKStringRef type, WKDictionaryRef properties)
{
#if ENABLE(MEDIA_STREAM)
String typeString = WebKit::toImpl(type)->string();
std::variant<WebCore::MockMicrophoneProperties, WebCore::MockSpeakerProperties, WebCore::MockCameraProperties, WebCore::MockDisplayProperties> properties;
if (typeString == "camera"_s)
properties = WebCore::MockCameraProperties { };
else if (typeString == "screen"_s)
properties = WebCore::MockDisplayProperties { };
std::variant<WebCore::MockMicrophoneProperties, WebCore::MockSpeakerProperties, WebCore::MockCameraProperties, WebCore::MockDisplayProperties> deviceProperties;
if (typeString == "camera"_s) {
WebCore::MockCameraProperties cameraProperties;
if (properties) {
auto facingModeKey = adoptWK(WKStringCreateWithUTF8CString("facingMode"));
if (auto facingMode = WKDictionaryGetItemForKey(properties, facingModeKey.get())) {
if (WKStringIsEqualToUTF8CString(static_cast<WKStringRef>(facingMode), "unknown"))
cameraProperties.facingMode = WebCore::VideoFacingMode::Unknown;
}
auto fillColorKey = adoptWK(WKStringCreateWithUTF8CString("fillColor"));
if (auto fillColor = WKDictionaryGetItemForKey(properties, fillColorKey.get())) {
if (WKStringIsEqualToUTF8CString(static_cast<WKStringRef>(fillColor), "green"))
cameraProperties.fillColor = WebCore::Color::green;
}
}
deviceProperties = WTFMove(cameraProperties);
} else if (typeString == "screen"_s)
deviceProperties = WebCore::MockDisplayProperties { };
else if (typeString == "speaker"_s)
properties = WebCore::MockSpeakerProperties { };
deviceProperties = WebCore::MockSpeakerProperties { };
else if (typeString != "microphone"_s)
return;

toImpl(context)->addMockMediaDevice({ WebKit::toImpl(persistentId)->string(), WebKit::toImpl(label)->string(), false, WTFMove(properties) });
toImpl(context)->addMockMediaDevice({ WebKit::toImpl(persistentId)->string(), WebKit::toImpl(label)->string(), false, WTFMove(deviceProperties) });
#endif
}

Expand Down
2 changes: 1 addition & 1 deletion Source/WebKit/UIProcess/API/C/WKMockMediaDevice.h
Expand Up @@ -32,7 +32,7 @@
extern "C" {
#endif

WK_EXPORT void WKAddMockMediaDevice(WKContextRef, WKStringRef persistentId, WKStringRef label, WKStringRef type);
WK_EXPORT void WKAddMockMediaDevice(WKContextRef, WKStringRef persistentId, WKStringRef label, WKStringRef type, WKDictionaryRef properties);
WK_EXPORT void WKClearMockMediaDevices(WKContextRef);
WK_EXPORT void WKRemoveMockMediaDevice(WKContextRef, WKStringRef persistentId);
WK_EXPORT void WKResetMockMediaDevices(WKContextRef);
Expand Down
Expand Up @@ -399,7 +399,7 @@ interface TestRunner {

undefined installFakeHelvetica(DOMString configuration);

undefined addMockCameraDevice(DOMString persistentId, DOMString label);
undefined addMockCameraDevice(DOMString persistentId, DOMString label, object properties);
undefined addMockMicrophoneDevice(DOMString persistentId, DOMString label);
undefined addMockScreenDevice(DOMString persistentId, DOMString label);
undefined clearMockMediaDevices();
Expand Down
37 changes: 32 additions & 5 deletions Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp
Expand Up @@ -1865,28 +1865,55 @@ void TestRunner::callDidReceiveLoadedSubresourceDomainsCallback(Vector<String>&&
callTestRunnerCallback(LoadedSubresourceDomainsCallbackID, 1, &result);
}

void TestRunner::addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type)
void TestRunner::addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type, WKDictionaryRef properties)
{
postSynchronousMessage("AddMockMediaDevice", createWKDictionary({
{ "PersistentID", toWK(persistentId) },
{ "Label", toWK(label) },
{ "Type", toWK(type) },
{ "Properties", properties },
}));
}

void TestRunner::addMockCameraDevice(JSStringRef persistentId, JSStringRef label)
void TestRunner::addMockCameraDevice(JSStringRef persistentId, JSStringRef label, JSValueRef properties)
{
addMockMediaDevice(persistentId, label, "camera");
auto context = mainFrameJSContext();

Vector<WKRetainPtr<WKStringRef>> strings;
Vector<WKStringRef> keys;
Vector<WKTypeRef> values;

if (auto object = JSValueToObject(context, properties, nullptr)) {
JSPropertyNameArrayRef propertyNameArray = JSObjectCopyPropertyNames(context, object);
size_t length = JSPropertyNameArrayGetCount(propertyNameArray);

for (size_t i = 0; i < length; ++i) {
JSStringRef jsPropertyName = JSPropertyNameArrayGetNameAtIndex(propertyNameArray, i);
auto jsPropertyValue = JSObjectGetProperty(context, object, jsPropertyName, 0);

auto propertyName = toWK(jsPropertyName);
auto propertyValue = toWKString(context, jsPropertyValue);

keys.append(propertyName.get());
values.append(propertyValue.get());
strings.append(WTFMove(propertyName));
strings.append(WTFMove(propertyValue));
}
JSPropertyNameArrayRelease(propertyNameArray);
}

auto wkProperties = adoptWK(WKDictionaryCreate(keys.data(), values.data(), keys.size()));
addMockMediaDevice(persistentId, label, "camera", wkProperties.get());
}

void TestRunner::addMockMicrophoneDevice(JSStringRef persistentId, JSStringRef label)
{
addMockMediaDevice(persistentId, label, "microphone");
addMockMediaDevice(persistentId, label, "microphone", nullptr);
}

void TestRunner::addMockScreenDevice(JSStringRef persistentId, JSStringRef label)
{
addMockMediaDevice(persistentId, label, "screen");
addMockMediaDevice(persistentId, label, "screen", nullptr);
}

void TestRunner::clearMockMediaDevices()
Expand Down
4 changes: 2 additions & 2 deletions Tools/WebKitTestRunner/InjectedBundle/TestRunner.h
Expand Up @@ -506,7 +506,7 @@ class TestRunner : public JSWrappable {
void dumpAllHTTPRedirectedResponseHeaders() { m_dumpAllHTTPRedirectedResponseHeaders = true; }
bool shouldDumpAllHTTPRedirectedResponseHeaders() const { return m_dumpAllHTTPRedirectedResponseHeaders; }

void addMockCameraDevice(JSStringRef persistentId, JSStringRef label);
void addMockCameraDevice(JSStringRef persistentId, JSStringRef label, JSValueRef properties);
void addMockMicrophoneDevice(JSStringRef persistentId, JSStringRef label);
void addMockScreenDevice(JSStringRef persistentId, JSStringRef label);
void clearMockMediaDevices();
Expand Down Expand Up @@ -578,7 +578,7 @@ class TestRunner : public JSWrappable {
void setDumpPixels(bool);
void setWaitUntilDone(bool);

void addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type);
void addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type, WKDictionaryRef);

WKRetainPtr<WKURLRef> m_testURL; // Set by InjectedBundlePage once provisional load starts.

Expand Down
4 changes: 2 additions & 2 deletions Tools/WebKitTestRunner/TestController.cpp
Expand Up @@ -3920,9 +3920,9 @@ void TestController::removeAllCookies()
m_currentInvocation->didRemoveAllCookies();
}

void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type)
void TestController::addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type, WKDictionaryRef properties)
{
WKAddMockMediaDevice(platformContext(), persistentID, label, type);
WKAddMockMediaDevice(platformContext(), persistentID, label, type, properties);
}

void TestController::clearMockMediaDevices()
Expand Down
2 changes: 1 addition & 1 deletion Tools/WebKitTestRunner/TestController.h
Expand Up @@ -323,7 +323,7 @@ class TestController {
bool didReceiveServerRedirectForProvisionalNavigation() const { return m_didReceiveServerRedirectForProvisionalNavigation; }
void clearDidReceiveServerRedirectForProvisionalNavigation() { m_didReceiveServerRedirectForProvisionalNavigation = false; }

void addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type);
void addMockMediaDevice(WKStringRef persistentID, WKStringRef label, WKStringRef type, WKDictionaryRef properties);
void clearMockMediaDevices();
void removeMockMediaDevice(WKStringRef persistentID);
void setMockMediaDeviceIsEphemeral(WKStringRef, bool);
Expand Down
3 changes: 2 additions & 1 deletion Tools/WebKitTestRunner/TestInvocation.cpp
Expand Up @@ -1006,7 +1006,8 @@ WKRetainPtr<WKTypeRef> TestInvocation::didReceiveSynchronousMessageFromInjectedB
auto persistentID = stringValue(messageBodyDictionary, "PersistentID");
auto label = stringValue(messageBodyDictionary, "Label");
auto type = stringValue(messageBodyDictionary, "Type");
TestController::singleton().addMockMediaDevice(persistentID, label, type);
auto properties = dictionaryValue(value(messageBodyDictionary, "Properties"));
TestController::singleton().addMockMediaDevice(persistentID, label, type, properties);
return nullptr;
}

Expand Down

0 comments on commit d0a00be

Please sign in to comment.