Title: [214385] trunk
Revision
214385
Author
eric.carl...@apple.com
Date
2017-03-24 16:27:14 -0700 (Fri, 24 Mar 2017)

Log Message

[MediaStream] "ideal" constraints passed to getUserMedia should affect fitness score
https://bugs.webkit.org/show_bug.cgi?id=170056

Reviewed by Youenn Fablet.

Source/WebCore:

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.

Source/WebKit2:

* 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.

LayoutTests:

* 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:

Modified Paths

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  <eric.carl...@apple.com>
+
+        [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  <d...@apple.com>
 
         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  <eric.carl...@apple.com>
+
+        [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  <d...@apple.com>
 
         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  <eric.carl...@apple.com>
+
+        [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  <simon.fra...@apple.com>
 
         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;
         }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to