Diff
Modified: trunk/LayoutTests/ChangeLog (214384 => 214385)
--- trunk/LayoutTests/ChangeLog 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/ChangeLog 2017-03-24 23:27:14 UTC (rev 214385)
@@ -1,3 +1,18 @@
+2017-03-24 Eric Carlson <[email protected]>
+
+ [MediaStream] "ideal" constraints passed to getUserMedia should affect fitness score
+ https://bugs.webkit.org/show_bug.cgi?id=170056
+
+ Reviewed by Youenn Fablet.
+
+ * fast/mediastream/MediaStream-video-element-displays-buffer-expected.txt:
+ * fast/mediastream/MediaStream-video-element-displays-buffer.html:
+ * fast/mediastream/MediaStreamTrack-getCapabilities-expected.txt:
+ * fast/mediastream/apply-constraints-advanced-expected.txt:
+ * fast/mediastream/apply-constraints-advanced.html:
+ * fast/mediastream/apply-constraints-video-expected.txt:
+ * fast/mediastream/apply-constraints-video.html:
+
2017-03-24 Dean Jackson <[email protected]>
Serialization of custom props in longhand should be "" not value of shorthand
Modified: trunk/LayoutTests/fast/mediastream/MediaStream-video-element-displays-buffer-expected.txt (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/MediaStream-video-element-displays-buffer-expected.txt 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/MediaStream-video-element-displays-buffer-expected.txt 2017-03-24 23:27:14 UTC (rev 214385)
@@ -3,15 +3,29 @@
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+ === checking pixels from front camera ===
PASS mediaDevices.getUserMedia generated a stream successfully.
-video.src = ""
-video.play()
+videos[0].src = ""
+videos[0].play()
+PASS isPixelTransparent(buffer) is true
+context.drawImage(videos[0], 0, 0, 680, 360)
+PASS isPixelTransparent(buffer) is false
+PASS isPixelBlack(buffer) is false
+PASS isPixelTransparent(buffer) is false
+PASS isPixelBlack(buffer) is true
- === checking pixels ===
+ === checking pixels from back camera ===
+PASS mediaDevices.getUserMedia generated a stream successfully.
+videos[1].src = ""
+videos[1].play()
PASS isPixelTransparent(buffer) is true
+context.drawImage(videos[1], 0, 0, 680, 360)
PASS isPixelTransparent(buffer) is false
PASS isPixelBlack(buffer) is false
+PASS isPixelTransparent(buffer) is false
+PASS isPixelGray(buffer) is true
PASS successfullyParsed is true
TEST COMPLETE
-
+
Modified: trunk/LayoutTests/fast/mediastream/MediaStream-video-element-displays-buffer.html (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/MediaStream-video-element-displays-buffer.html 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/MediaStream-video-element-displays-buffer.html 2017-03-24 23:27:14 UTC (rev 214385)
@@ -8,12 +8,13 @@
<p id="description"></p>
<div id="console"></div>
<video controls width="680" height="360"></video>
+<video controls width="680" height="360"></video>
<canvas width="680" height="360"></canvas>
<script>
let mediaStream;
- let video;
-
+ let videos;
let buffer;
+ let currentTest = 0;
function isPixelTransparent(pixel)
{
@@ -25,44 +26,78 @@
return pixel[0] === 0 && pixel[1] === 0 && pixel[2] === 0 && pixel[3] === 255;
}
+ function isPixelGray(pixel)
+ {
+ return pixel[0] === 128 && pixel[1] === 128 && pixel[2] === 128 && pixel[3] === 255;
+ }
+
function verifyFramesBeingDisplayed()
{
- let canvas = document.querySelector('canvas');
- let context = canvas.getContext('2d');
+ videos[currentTest].removeEventListener('playing', verifyFramesBeingDisplayed, false)
- debug('<br> === checking pixels ===');
-
+ canvas = document.querySelector('canvas');
+ context = canvas.getContext('2d');
+
context.clearRect(0, 0, canvas.width, canvas.height);
-
let x = canvas.width * .035;
let y = canvas.height * 0.6 + 2 + x;
-
buffer = context.getImageData(x, y, 1, 1).data;
shouldBeTrue('isPixelTransparent(buffer)');
- context.drawImage(video, 0, 0, canvas.width, canvas.height);
-
+ evalAndLog(`context.drawImage(videos[${currentTest}], 0, 0, ${canvas.width}, ${canvas.height})`);
buffer = context.getImageData(x, y, 1, 1).data;
shouldBeFalse('isPixelTransparent(buffer)');
shouldBeFalse('isPixelBlack(buffer)');
- finishJSTest();
+ x = canvas.width * .05;
+ y = canvas.height * .05;
+ buffer = context.getImageData(x, y, 1, 1).data;
+ shouldBeFalse('isPixelTransparent(buffer)');
+ if (!currentTest)
+ shouldBeTrue('isPixelBlack(buffer)');
+ else
+ shouldBeTrue('isPixelGray(buffer)');
+
+ if (currentTest >= 1) {
+ finishJSTest();
+ return;
+ }
+
+ videos[currentTest].pause();
+ ++currentTest;
+ requestNextStream();
}
+ function setupVideoElement(stream)
+ {
+ mediaStream = stream;
+ testPassed('mediaDevices.getUserMedia generated a stream successfully.');
+ evalAndLog(`videos[${currentTest}].src = ""
+ }
+
function canplay()
{
- evalAndLog('video.play()');
+ videos[currentTest].removeEventListener('canplay', canplay, false)
+ evalAndLog(`videos[${currentTest}].play()`);
}
-
+
+ function requestNextStream()
+ {
+ debug(`<br> === checking pixels from ${!currentTest ? "front" : "back"} camera ===`);
+ let constraints = {video : !currentTest ? true : {facingMode: "environment"}};
+ getUserMedia("allow", constraints, setupVideoElement);
+ }
+
function start()
{
description("Tests that the stream displays captured buffers to the video element.");
- video = document.querySelector('video');
- video.addEventListener('canplay', canplay, false);
- video.addEventListener('playing', verifyFramesBeingDisplayed, false);
-
- getUserMedia("allow", {video:true}, setupVideoElementWithStream);
+ videos = Array.from(document.getElementsByTagName('video'));
+ videos.forEach((video) => {
+ video.addEventListener('canplay', canplay, false);
+ video.addEventListener('playing', verifyFramesBeingDisplayed, false);
+ });
+ requestNextStream();
}
window.jsTestIsAsync = true;
Modified: trunk/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities-expected.txt (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities-expected.txt 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/MediaStreamTrack-getCapabilities-expected.txt 2017-03-24 23:27:14 UTC (rev 214385)
@@ -6,7 +6,7 @@
video track capabilities:
capabilities.aspectRatio = { max: 1.778, min: 1.333 }
capabilities.deviceId = <UUID>
- capabilities.facingMode = [ user, environment ]
+ capabilities.facingMode = [ user ]
capabilities.frameRate = { max: 60, min: 15 }
capabilities.height = { max: 1080, min: 240 }
capabilities.width = { max: 1920, min: 320 }
Modified: trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt 2017-03-24 23:27:14 UTC (rev 214385)
@@ -39,8 +39,8 @@
** Constraint: {"advanced":[{"facingMode":"left"},{"facingMode":"right"},{"facingMode":"environment"},{"facingMode":"user"}]} - no required constraints, advanced constraints are ignored.
PASS settings['facingMode'] is "user"
-** Constraint: {"width":{"min":640},"advanced":[{"facingMode":"left"},{"facingMode":"right"},{"facingMode":"environment"},{"facingMode":"user"}]} - first two advanced facingModes are not supported, third is used.
-PASS settings['facingMode'] is "environment"
+** Constraint: {"width":{"min":640},"advanced":[{"facingMode":"left"},{"facingMode":"right"},{"facingMode":"user"}]} - first two advanced facingModes are not supported, third is used.
+PASS settings['facingMode'] is "user"
PASS successfullyParsed is true
Modified: trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html 2017-03-24 23:27:14 UTC (rev 214385)
@@ -86,11 +86,10 @@
advanced: [
{ facingMode: "left" },
{ facingMode: "right" },
- { facingMode: "environment" },
{ facingMode: "user" },
]
},
- expected: { facingMode: "environment" },
+ expected: { facingMode: "user" },
},
];
Modified: trunk/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt 2017-03-24 23:27:14 UTC (rev 214385)
@@ -57,18 +57,18 @@
PASS settings['height'] is 240
PASS settings['frameRate'] is 15
-** Constraint: {"facingMode":"environment"} - set facing mode, width and height should remain unchanged
+** Constraint: {"frameRate":20} - set frame rate, width and height should remain unchanged
PASS settings['width'] is 320
PASS settings['height'] is 240
-PASS settings['facingMode'] is "environment"
+PASS settings['frameRate'] is 20
-** Constraint: {"facingMode":"USER","height":400} - illegal facing mode value should be ignored, height should change.
-PASS settings['facingMode'] is "environment"
+** Constraint: {"facingMode":"environment","height":400} - illegal facing mode value should be ignored, height should change.
+PASS settings['facingMode'] is "user"
PASS settings['width'] is 320
PASS settings['height'] is 400
-** Constraint: {"FACINGMODE":"user","frameRate":30} - unknown constraint should be ignored, frame rate should change.
-PASS settings['facingMode'] is "environment"
+** Constraint: {"WITDH":400,"frameRate":30} - unknown constraint should be ignored, frame rate should change.
+PASS settings['width'] is 320
PASS settings['frameRate'] is 30
** Constraint: {"aspectRatio":"1.3333"} - aspect ratio should change width and height.
Modified: trunk/LayoutTests/fast/mediastream/apply-constraints-video.html (214384 => 214385)
--- trunk/LayoutTests/fast/mediastream/apply-constraints-video.html 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-video.html 2017-03-24 23:27:14 UTC (rev 214385)
@@ -60,19 +60,19 @@
expected: { width: 320, height: 240, frameRate: 15 },
},
{
- message: "set facing mode, width and height should remain unchanged",
- constraint: { facingMode: "environment" },
- expected: { width: 320, height: 240, facingMode: "environment" },
+ message: "set frame rate, width and height should remain unchanged",
+ constraint: { frameRate: 20 },
+ expected: { width: 320, height: 240, frameRate: 20 },
},
{
message: "illegal facing mode value should be ignored, height should change.",
- constraint: { facingMode: "USER", height: 400 },
- expected: { facingMode: "environment", width: 320, height: 400 },
+ constraint: { facingMode: "environment", height: 400 },
+ expected: { facingMode: "user", width: 320, height: 400 },
},
{
message: "unknown constraint should be ignored, frame rate should change.",
- constraint: { FACINGMODE: "user", frameRate: 30 },
- expected: { facingMode: "environment", frameRate: 30 },
+ constraint: { WITDH: 400, frameRate: 30 },
+ expected: { width: 320, frameRate: 30 },
},
{
message: "aspect ratio should change width and height.",
Modified: trunk/Source/WebCore/ChangeLog (214384 => 214385)
--- trunk/Source/WebCore/ChangeLog 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebCore/ChangeLog 2017-03-24 23:27:14 UTC (rev 214385)
@@ -1,3 +1,35 @@
+2017-03-24 Eric Carlson <[email protected]>
+
+ [MediaStream] "ideal" constraints passed to getUserMedia should affect fitness score
+ https://bugs.webkit.org/show_bug.cgi?id=170056
+
+ Reviewed by Youenn Fablet.
+
+ Include the fitness score calculated for ideal constraints in the calculation of a capture
+ overall device fitness score.
+
+ No new tests, existing tests updated.
+
+ * platform/mediastream/MediaConstraints.cpp:
+ (WebCore::StringConstraint::fitnessDistance): Drive-by fix: return early if ideal is empty,
+ not exact.
+
+ * platform/mediastream/RealtimeMediaSource.cpp:
+ (WebCore::RealtimeMediaSource::supportsSizeAndFrameRate): Return fitness distance.
+ (WebCore::RealtimeMediaSource::selectSettings): Include the fitness distance of supported
+ ideal constraints.
+ (WebCore::RealtimeMediaSource::supportsConstraint): New.
+ (WebCore::RealtimeMediaSource::applyConstraints):
+ * platform/mediastream/RealtimeMediaSource.h:
+
+ * platform/mock/MockRealtimeMediaSourceCenter.cpp:
+ (WebCore::MockRealtimeMediaSourceCenter::validateRequestConstraints): Sort candidate sources
+ by their fitness score.
+
+ * platform/mock/MockRealtimeVideoSource.cpp:
+ (WebCore::MockRealtimeVideoSource::initializeCapabilities): Each video source should support
+ one facing mode, not both.
+
2017-03-24 Dean Jackson <[email protected]>
Serialization of custom props in longhand should be "" not value of shorthand
Modified: trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp (214384 => 214385)
--- trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp 2017-03-24 23:27:14 UTC (rev 214385)
@@ -68,7 +68,7 @@
return std::numeric_limits<double>::infinity();
// 3. If no ideal value is specified, the fitness distance is 0.
- if (m_exact.isEmpty())
+ if (m_ideal.isEmpty())
return 0;
// 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp (214384 => 214385)
--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp 2017-03-24 23:27:14 UTC (rev 214385)
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
* Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2015 Ericsson AB. All rights reserved.
*
@@ -176,7 +176,7 @@
return true;
}
-bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<IntConstraint> widthConstraint, std::optional<IntConstraint> heightConstraint, std::optional<DoubleConstraint> frameRateConstraint, String& badConstraint)
+bool RealtimeMediaSource::supportsSizeAndFrameRate(std::optional<IntConstraint> widthConstraint, std::optional<IntConstraint> heightConstraint, std::optional<DoubleConstraint> frameRateConstraint, String& badConstraint, double& distance)
{
if (!widthConstraint && !heightConstraint && !frameRateConstraint)
return true;
@@ -184,13 +184,17 @@
ASSERT(this->capabilities());
RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
+ distance = std::numeric_limits<double>::infinity();
+
std::optional<int> width;
if (widthConstraint && capabilities.supportsWidth()) {
- if (std::isinf(fitnessDistance(*widthConstraint))) {
+ double constraintDistance = fitnessDistance(*widthConstraint);
+ if (std::isinf(constraintDistance)) {
badConstraint = widthConstraint->name();
return false;
}
+ distance = std::min(distance, constraintDistance);
auto range = capabilities.width();
width = widthConstraint->valueForCapabilityRange(size().width(), range.rangeMin().asInt, range.rangeMax().asInt);
}
@@ -197,11 +201,13 @@
std::optional<int> height;
if (heightConstraint && capabilities.supportsHeight()) {
- if (std::isinf(fitnessDistance(*heightConstraint))) {
+ double constraintDistance = fitnessDistance(*heightConstraint);
+ if (std::isinf(constraintDistance)) {
badConstraint = heightConstraint->name();
return false;
}
+ distance = std::min(distance, constraintDistance);
auto range = capabilities.height();
height = heightConstraint->valueForCapabilityRange(size().height(), range.rangeMin().asInt, range.rangeMax().asInt);
}
@@ -208,16 +214,18 @@
std::optional<double> frameRate;
if (frameRateConstraint && capabilities.supportsFrameRate()) {
- if (std::isinf(fitnessDistance(*frameRateConstraint))) {
+ double constraintDistance = fitnessDistance(*frameRateConstraint);
+ if (std::isinf(constraintDistance)) {
badConstraint = frameRateConstraint->name();
return false;
}
+ distance = std::min(distance, constraintDistance);
auto range = capabilities.frameRate();
frameRate = frameRateConstraint->valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
}
- // Each of the values is supported individually, see if they all can be applied at the same time.
+ // Each of the non-null values is supported individually, see if they all can be applied at the same time.
if (!supportsSizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate))) {
if (widthConstraint)
badConstraint = widthConstraint->name();
@@ -495,6 +503,8 @@
bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
{
+ m_fitnessScore = std::numeric_limits<double>::infinity();
+
// https://w3c.github.io/mediacapture-main/#dfn-selectsettings
//
// 1. Each constraint specifies one or more values (or a range of values) for its property.
@@ -517,20 +527,26 @@
failedConstraint = emptyString();
// Check width, height, and frame rate separately, because while they may be supported individually the combination may not be supported.
- if (!supportsSizeAndFrameRate(constraints.mandatoryConstraints().width(), constraints.mandatoryConstraints().height(), constraints.mandatoryConstraints().frameRate(), failedConstraint))
+ double distance = std::numeric_limits<double>::infinity();
+ if (!supportsSizeAndFrameRate(constraints.mandatoryConstraints().width(), constraints.mandatoryConstraints().height(), constraints.mandatoryConstraints().frameRate(), failedConstraint, m_fitnessScore))
return false;
constraints.mandatoryConstraints().filter([&](const MediaConstraint& constraint) {
+ if (!supportsConstraint(constraint))
+ return false;
+
if (constraint.constraintType() == MediaConstraintType::Width || constraint.constraintType() == MediaConstraintType::Height || constraint.constraintType() == MediaConstraintType::FrameRate) {
candidates.set(constraint);
return false;
}
- if (std::isinf(fitnessDistance(constraint))) {
+ double constraintDistance = fitnessDistance(constraint);
+ if (std::isinf(constraintDistance)) {
failedConstraint = constraint.name();
return true;
}
+ distance = std::min(distance, constraintDistance);
candidates.set(constraint);
return false;
});
@@ -538,6 +554,8 @@
if (!failedConstraint.isEmpty())
return false;
+ m_fitnessScore = distance;
+
// 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
if (candidates.isEmpty())
return true;
@@ -548,7 +566,6 @@
// 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
// values of properties as exact.
Vector<std::pair<double, MediaTrackConstraintSetMap>> supportedConstraints;
- m_fitnessScore = std::numeric_limits<double>::infinity();
for (const auto& advancedConstraint : constraints.advancedConstraints()) {
double constraintDistance = 0;
@@ -555,14 +572,13 @@
bool supported = false;
advancedConstraint.forEach([&](const MediaConstraint& constraint) {
- double distance = fitnessDistance(constraint);
+ distance = fitnessDistance(constraint);
constraintDistance += distance;
if (!std::isinf(distance))
supported = true;
});
- if (constraintDistance < m_fitnessScore)
- m_fitnessScore = constraintDistance;
+ m_fitnessScore = std::min(m_fitnessScore, constraintDistance);
// 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
// settings dictionaries in candidates, discarding others.
@@ -573,9 +589,9 @@
// 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
// The UA should use the one with the smallest fitness distance, as calculated in step 3.
- if (!std::isinf(m_fitnessScore)) {
+ if (!supportedConstraints.isEmpty()) {
supportedConstraints.removeAllMatching([&](const std::pair<double, MediaTrackConstraintSetMap>& pair) -> bool {
- return pair.first > m_fitnessScore;
+ return std::isinf(pair.first) || pair.first > m_fitnessScore;
});
if (!supportedConstraints.isEmpty()) {
@@ -583,6 +599,8 @@
advancedConstraint.forEach([&](const MediaConstraint& constraint) {
candidates.merge(constraint);
});
+
+ m_fitnessScore = std::min(m_fitnessScore, supportedConstraints[0].first);
}
}
@@ -589,6 +607,75 @@
return true;
}
+bool RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint) const
+{
+ ASSERT(this->capabilities());
+ RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
+
+ switch (constraint.constraintType()) {
+ case MediaConstraintType::Width:
+ ASSERT(constraint.isInt());
+ return capabilities.supportsWidth();
+ break;
+
+ case MediaConstraintType::Height:
+ ASSERT(constraint.isInt());
+ return capabilities.supportsHeight();
+ break;
+
+ case MediaConstraintType::FrameRate:
+ ASSERT(constraint.isDouble());
+ return capabilities.supportsFrameRate();
+ break;
+
+ case MediaConstraintType::AspectRatio:
+ ASSERT(constraint.isDouble());
+ return capabilities.supportsAspectRatio();
+ break;
+
+ case MediaConstraintType::Volume:
+ ASSERT(constraint.isDouble());
+ return capabilities.supportsVolume();
+ break;
+
+ case MediaConstraintType::SampleRate:
+ ASSERT(constraint.isInt());
+ return capabilities.supportsSampleRate();
+ break;
+
+ case MediaConstraintType::SampleSize:
+ ASSERT(constraint.isInt());
+ return capabilities.supportsSampleSize();
+ break;
+
+ case MediaConstraintType::FacingMode:
+ ASSERT(constraint.isString());
+ return capabilities.supportsFacingMode();
+ break;
+
+ case MediaConstraintType::EchoCancellation:
+ ASSERT(constraint.isBoolean());
+ return capabilities.supportsEchoCancellation();
+ break;
+
+ case MediaConstraintType::DeviceId:
+ ASSERT(constraint.isString());
+ return capabilities.supportsDeviceId();
+ break;
+
+ case MediaConstraintType::GroupId:
+ ASSERT(constraint.isString());
+ return capabilities.supportsDeviceId();
+ break;
+
+ case MediaConstraintType::Unknown:
+ // Unknown (or unsupported) constraints should be ignored.
+ break;
+ }
+
+ return false;
+}
+
bool RealtimeMediaSource::supportsConstraints(const MediaConstraints& constraints, String& invalidConstraint)
{
ASSERT(constraints.isValid());
@@ -635,6 +722,9 @@
frameRate = downcast<DoubleConstraint>(*constraint).valueForCapabilityRange(this->frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble);
}
}
+
+ // FIXME: applySizeAndFrameRate should take MediaConstraint* instead of std::optional<> so it can see if a constraint is an exact, min, max,
+ // or ideal, and choose the correct value for properties with non-discreet capabilities when necessary.
if (width || height || frameRate)
applySizeAndFrameRate(WTFMove(width), WTFMove(height), WTFMove(frameRate));
Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h (214384 => 214385)
--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h 2017-03-24 23:27:14 UTC (rev 214385)
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2011 Ericsson AB. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 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
@@ -116,6 +116,7 @@
std::optional<std::pair<String, String>> applyConstraints(const MediaConstraints&);
virtual bool supportsConstraints(const MediaConstraints&, String&);
+ virtual bool supportsConstraint(const MediaConstraint&) const;
virtual void settingsDidChange();
@@ -192,7 +193,7 @@
virtual bool selectSettings(const MediaConstraints&, FlattenedConstraint&, String&);
virtual double fitnessDistance(const MediaConstraint&);
- virtual bool supportsSizeAndFrameRate(std::optional<IntConstraint> width, std::optional<IntConstraint> height, std::optional<DoubleConstraint>, String&);
+ virtual bool supportsSizeAndFrameRate(std::optional<IntConstraint> width, std::optional<IntConstraint> height, std::optional<DoubleConstraint>, String&, double& fitnessDistance);
virtual bool supportsSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double>);
virtual void applyConstraint(const MediaConstraint&);
virtual void applyConstraints(const FlattenedConstraint&);
Modified: trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp (214384 => 214385)
--- trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp 2017-03-24 23:27:14 UTC (rev 214385)
@@ -72,32 +72,56 @@
Vector<String> videoSourceIds;
String invalidConstraint;
+ struct DeviceInfo {
+ unsigned fitnessScore;
+ String id;
+ };
+
+ struct {
+ bool operator()(const DeviceInfo& a, const DeviceInfo& b)
+ {
+ return a.fitnessScore < b.fitnessScore;
+ }
+ } sortBasedOnFitnessScore;
+
if (audioConstraints.isValid()) {
- auto& devices = MockRealtimeMediaSource::audioDevices();
- for (size_t i = 0; i < devices.size(); i++) {
- auto& device = devices[i];
+ Vector<DeviceInfo> deviceInfo;
+ for (const auto& device : MockRealtimeMediaSource::audioDevices()) {
auto audioSource = MockRealtimeAudioSource::create(device.label(), nullptr);
- if (!audioSource->supportsConstraints(audioConstraints, invalidConstraint)) {
- if (invalidHandler)
- invalidHandler(invalidConstraint);
- return;
- }
- audioSourceIds.append(device.persistentId());
+ if (audioSource->supportsConstraints(audioConstraints, invalidConstraint))
+ deviceInfo.append({audioSource->fitnessScore(), device.persistentId()});
}
+
+ if (deviceInfo.isEmpty()) {
+ if (invalidHandler)
+ invalidHandler(invalidConstraint);
+ return;
+ }
+
+ audioSourceIds.reserveInitialCapacity(deviceInfo.size());
+ std::sort(deviceInfo.begin(), deviceInfo.end(), sortBasedOnFitnessScore);
+ for (const auto& info : deviceInfo)
+ audioSourceIds.uncheckedAppend(info.id);
}
if (videoConstraints.isValid()) {
- auto& devices = MockRealtimeMediaSource::videoDevices();
- for (size_t i = 0; i < devices.size(); i++) {
- auto& device = devices[i];
+ Vector<DeviceInfo> deviceInfo;
+ for (const auto& device : MockRealtimeMediaSource::videoDevices()) {
auto videoSource = MockRealtimeVideoSource::create(device.label(), nullptr);
- if (!videoSource->supportsConstraints(videoConstraints, invalidConstraint)) {
- if (invalidHandler)
- invalidHandler(invalidConstraint);
- return;
- }
- videoSourceIds.append(device.persistentId());
+ if (videoSource->supportsConstraints(videoConstraints, invalidConstraint))
+ deviceInfo.append({videoSource->fitnessScore(), device.persistentId()});
}
+
+ if (deviceInfo.isEmpty()) {
+ if (invalidHandler)
+ invalidHandler(invalidConstraint);
+ return;
+ }
+
+ videoSourceIds.reserveInitialCapacity(deviceInfo.size());
+ std::sort(deviceInfo.begin(), deviceInfo.end(), sortBasedOnFitnessScore);
+ for (const auto& info : deviceInfo)
+ videoSourceIds.uncheckedAppend(info.id);
}
validHandler(WTFMove(audioSourceIds), WTFMove(videoSourceIds));
Modified: trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp (214384 => 214385)
--- trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebCore/platform/mock/MockRealtimeVideoSource.cpp 2017-03-24 23:27:14 UTC (rev 214385)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. 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
@@ -133,8 +133,10 @@
void MockRealtimeVideoSource::initializeCapabilities(RealtimeMediaSourceCapabilities& capabilities)
{
- capabilities.addFacingMode(RealtimeMediaSourceSettings::User);
- capabilities.addFacingMode(RealtimeMediaSourceSettings::Environment);
+ if (!deviceIndex())
+ capabilities.addFacingMode(RealtimeMediaSourceSettings::User);
+ else
+ capabilities.addFacingMode(RealtimeMediaSourceSettings::Environment);
capabilities.setWidth(CapabilityValueOrRange(320, 1920));
capabilities.setHeight(CapabilityValueOrRange(240, 1080));
capabilities.setFrameRate(CapabilityValueOrRange(15.0, 60.0));
@@ -348,7 +350,7 @@
IntSize size = this->size();
FloatRect frameRect(FloatPoint(), size);
- context.fillRect(FloatRect(FloatPoint(), size), facingMode() == RealtimeMediaSourceSettings::User ? Color::black : Color::gray);
+ context.fillRect(FloatRect(FloatPoint(), size), !deviceIndex() ? Color::black : Color::darkGray);
if (!m_muted && m_enabled) {
drawText(context);
Modified: trunk/Source/WebKit2/ChangeLog (214384 => 214385)
--- trunk/Source/WebKit2/ChangeLog 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebKit2/ChangeLog 2017-03-24 23:27:14 UTC (rev 214385)
@@ -1,3 +1,15 @@
+2017-03-24 Eric Carlson <[email protected]>
+
+ [MediaStream] "ideal" constraints passed to getUserMedia should affect fitness score
+ https://bugs.webkit.org/show_bug.cgi?id=170056
+
+ Reviewed by Youenn Fablet.
+
+ * UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
+ (WebKit::UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame): When
+ short-circuiting the user prompt because the page is already authorized, return the first
+ audio and/or video device because so the page gets the one with the best fitness distance.
+
2017-03-24 Simon Fraser <[email protected]>
Make UI-side compositing on macOS a bit more usable
Modified: trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.cpp (214384 => 214385)
--- trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.cpp 2017-03-24 23:25:16 UTC (rev 214384)
+++ trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.cpp 2017-03-24 23:27:14 UTC (rev 214385)
@@ -242,24 +242,24 @@
auto topLevelOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(topLevelDocumentOriginIdentifier)->securityOrigin());
auto request = createRequest(userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier, audioDeviceUIDs, videoDeviceUIDs);
- String authorizedAudioDevice;
- String authorizedVideoDevice;
+ bool authorizedForAudio = false;
+ bool authorizedForVideo = false;
auto& fameState = stateForRequest(request);
for (auto deviceUID : audioDeviceUIDs) {
if (fameState.hasPermissionToUseCaptureDevice(deviceUID)) {
- authorizedAudioDevice = deviceUID;
+ authorizedForAudio = true;
break;
}
}
for (auto deviceUID : videoDeviceUIDs) {
if (fameState.hasPermissionToUseCaptureDevice(deviceUID)) {
- authorizedVideoDevice = deviceUID;
+ authorizedForVideo = true;
break;
}
}
- if (audioDeviceUIDs.isEmpty() == authorizedAudioDevice.isEmpty() && videoDeviceUIDs.isEmpty() == authorizedVideoDevice.isEmpty()) {
- userMediaAccessWasGranted(userMediaID, authorizedAudioDevice, authorizedVideoDevice);
+ if (authorizedForAudio == !audioDeviceUIDs.isEmpty() && authorizedForVideo == !videoDeviceUIDs.isEmpty()) {
+ userMediaAccessWasGranted(userMediaID, authorizedForAudio ? audioDeviceUIDs[0] : emptyString(), authorizedForVideo ? videoDeviceUIDs[0] : emptyString());
return;
}