<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[205574] trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/205574">205574</a></dd>
<dt>Author</dt> <dd>eric.carlson@apple.com</dd>
<dt>Date</dt> <dd>2016-09-07 17:01:29 -0700 (Wed, 07 Sep 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>[MediaStream] applyConstraints pt. 2 - advanced constraints
https://bugs.webkit.org/show_bug.cgi?id=161715
<rdar://problem/28195461>
Reviewed by Dean Jackson.
Source/WebCore:
Test: fast/mediastream/apply-constraints-advanced.html
* platform/mediastream/MediaConstraints.cpp:
(WebCore::MediaConstraint::create): Return Ref<>, not RefPtr<>.
(WebCore::MediaConstraint::copy): New
(WebCore::IntConstraint::copy): Ditto.
(WebCore::DoubleConstraint::copy): Ditto.
(WebCore::BooleanConstraint::copy): Ditto.
(WebCore::StringConstraint::copy): Ditto.
(WebCore::StringConstraint::fitnessDistance): New, compute the fitness distance between the
constraint and the specified value.
(WebCore::StringConstraint::merge): New, merge value into constraint.
(WebCore::FlattenedConstraint::set): New, add or replace a constraint.
(WebCore::FlattenedConstraint::merge): New, merge or add a constraint.
* platform/mediastream/MediaConstraints.h:
(WebCore::MediaConstraint::fitnessDistance):
(WebCore::MediaConstraint::merge):
(WebCore::NumericConstraint::nearlyEqual):
(WebCore::FlattenedConstraint::isEmpty):
(WebCore::FlattenedConstraint::begin):
(WebCore::FlattenedConstraint::end):
(WebCore::MediaConstraint::~MediaConstraint): Deleted.
(WebCore::MediaConstraint::find): Deleted.
(WebCore::MediaConstraint::getIdeal): Deleted.
* platform/mediastream/RealtimeMediaSource.cpp:
(WebCore::RealtimeMediaSource::fitnessDistance): Return the fitness distance between the source
capabilities and a constraint.
(WebCore::applyNumericConstraint): New, apply a numeric constraint.
(WebCore::RealtimeMediaSource::applyConstraint): Use applyNumericConstraint.
(WebCore::RealtimeMediaSource::selectSettings): New, implement the SelectSettings algorithm
(WebCore::RealtimeMediaSource::applyConstraints):
(WebCore::RealtimeMediaSource::supportsConstraint): Deleted.
(WebCore::value): Deleted.
* platform/mediastream/RealtimeMediaSource.h:
LayoutTests:
* fast/mediastream/apply-constraints-advanced-expected.txt: Added.
* fast/mediastream/apply-constraints-advanced.html: Added.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamMediaConstraintscpp">trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamMediaConstraintsh">trunk/Source/WebCore/platform/mediastream/MediaConstraints.h</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamRealtimeMediaSourcecpp">trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamRealtimeMediaSourceh">trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastmediastreamapplyconstraintsadvancedexpectedtxt">trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastmediastreamapplyconstraintsadvancedhtml">trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/LayoutTests/ChangeLog        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2016-09-07 Eric Carlson <eric.carlson@apple.com>
+
+ [MediaStream] applyConstraints pt. 2 - advanced constraints
+ https://bugs.webkit.org/show_bug.cgi?id=161715
+ <rdar://problem/28195461>
+
+ Reviewed by Dean Jackson.
+
+ * fast/mediastream/apply-constraints-advanced-expected.txt: Added.
+ * fast/mediastream/apply-constraints-advanced.html: Added.
+
</ins><span class="cx"> 2016-09-06 Dean Jackson <dino@apple.com>
</span><span class="cx">
</span><span class="cx"> Expose Apple Pencil input to testing system
</span></span></pre></div>
<a id="trunkLayoutTestsfastmediastreamapplyconstraintsadvancedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt (0 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt         (rev 0)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+Tests applyConstraints on a video stream track.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS stream.getVideoTracks().length is 1
+PASS stream.getAudioTracks().length is 0
+PASS video.videoTracks.length is 1
+PASS video.audioTracks.length is 0
+
+** Constraint: {"width":640,"height":480} - setup width and height.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+
+** Constraint: {"width":{"min":320},"height":{"min":240},"advanced":[{"width":1920,"height":1280}]} - advanced width and height are too big, minimums are less than current, nothing is changed.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+
+** Constraint: {"width":{"min":640},"height":{"min":480},"advanced":[{"width":1920,"height":1280},{"width":960,"height":720}]} - first width and height in advanced are too big, second is used.
+PASS settings['width'] is 960
+PASS settings['height'] is 720
+
+** Constraint: {"width":320,"height":240} - reset width and height.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+
+** Constraint: {"width":{"min":640},"height":{"min":480},"advanced":[{"width":1920,"height":1280}]} - advanced width and height are too big, fall back to required minimums.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+
+** Constraint: {"width":320,"height":240} - reset width and height.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+
+** Constraint: {"width":{"min":640},"height":{"min":480},"advanced":[{"width":1920,"height":1280},{"aspectRatio":1.777777}]} - advanced width and height are too big, aspectRatio is used.
+PASS settings['width'] is 640
+PASS settings['height'] is 360
+
+** 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"
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastmediastreamapplyconstraintsadvancedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html (0 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html         (rev 0)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -0,0 +1,121 @@
</span><ins>+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="../../resources/js-test-pre.js"></script>
+ <script src="resources/apply-constraints-utils.js"></script>
+ <script>
+
+ let tests = [
+ {
+ message: "setup width and height.",
+ constraint: { width: 640, height: 480 },
+ expected: { width: 640, height: 480 },
+ },
+ {
+ message: "advanced width and height are too big, minimums are less than current, nothing is changed.",
+ constraint: {
+ width: { min: 320 },
+ height: { min: 240 },
+ advanced: [
+ { width: 1920, height: 1280 },
+ ]
+ },
+ expected: { width: 640, height: 480 },
+ },
+ {
+ message: "first width and height in advanced are too big, second is used.",
+ constraint: {
+ width: { min: 640 },
+ height: { min: 480 },
+ advanced: [
+ { width: 1920, height: 1280 },
+ { width: 960, height: 720 },
+ ]
+ },
+ expected: { width: 960, height: 720 },
+ },
+ {
+ message: "reset width and height.",
+ constraint: { width: 320, height: 240 },
+ expected: { width: 320, height: 240 },
+ },
+ {
+ message: "advanced width and height are too big, fall back to required minimums.",
+ constraint: {
+ width: { min: 640 },
+ height: { min: 480 },
+ advanced: [
+ { width: 1920, height: 1280 },
+ ]
+ },
+ expected: { width: 640, height: 480 },
+ },
+ {
+ message: "reset width and height.",
+ constraint: { width: 320, height: 240 },
+ expected: { width: 320, height: 240 },
+ },
+ {
+ message: "advanced width and height are too big, aspectRatio is used.",
+ constraint: {
+ width: { min: 640 },
+ height: { min: 480 },
+ advanced: [
+ { width: 1920, height: 1280 },
+ { aspectRatio: 1.777777 }
+ ]
+ },
+ expected: { width: 640, height: 360 },
+ },
+ {
+ message: "no required constraints, advanced constraints are ignored.",
+ constraint: {
+ advanced: [
+ { facingMode: "left" },
+ { facingMode: "right" },
+ { facingMode: "environment" },
+ { facingMode: "user" },
+ ]
+ },
+ expected: { facingMode: "user" },
+ },
+ {
+ message: "first two advanced facingModes are not supported, third is used.",
+ constraint: {
+ width: { min: 640 },
+ advanced: [
+ { facingMode: "left" },
+ { facingMode: "right" },
+ { facingMode: "environment" },
+ { facingMode: "user" },
+ ]
+ },
+ expected: { facingMode: "environment" },
+ },
+ ];
+
+ let tester = new ConstraintsTest({ video: true }, tests, "Tests applyConstraints on a video stream track.")
+ .onStreamReady((s) => {
+ stream = s;
+ shouldBe('stream.getVideoTracks().length', '1');
+ shouldBe('stream.getAudioTracks().length', '0');
+ tester.setStreamTrack(stream.getVideoTracks()[0]);
+ })
+ .onVideoReady((v) => {
+ video = v;
+ shouldBe('video.videoTracks.length', '1');
+ shouldBe('video.audioTracks.length', '0');
+ })
+ .start();
+
+ </script>
+ <script src="../../resources/js-test-post.js"></script>
+ </head>
+ <body>
+ <video controls id="video"</video>
+ <br>
+ <div id="div"></div>
+
+ </body>
+</html>
+
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/ChangeLog        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2016-09-07 Eric Carlson <eric.carlson@apple.com>
+
+ [MediaStream] applyConstraints pt. 2 - advanced constraints
+ https://bugs.webkit.org/show_bug.cgi?id=161715
+ <rdar://problem/28195461>
+
+ Reviewed by Dean Jackson.
+
+ Test: fast/mediastream/apply-constraints-advanced.html
+
+ * platform/mediastream/MediaConstraints.cpp:
+ (WebCore::MediaConstraint::create): Return Ref<>, not RefPtr<>.
+ (WebCore::MediaConstraint::copy): New
+ (WebCore::IntConstraint::copy): Ditto.
+ (WebCore::DoubleConstraint::copy): Ditto.
+ (WebCore::BooleanConstraint::copy): Ditto.
+ (WebCore::StringConstraint::copy): Ditto.
+ (WebCore::StringConstraint::fitnessDistance): New, compute the fitness distance between the
+ constraint and the specified value.
+ (WebCore::StringConstraint::merge): New, merge value into constraint.
+ (WebCore::FlattenedConstraint::set): New, add or replace a constraint.
+ (WebCore::FlattenedConstraint::merge): New, merge or add a constraint.
+ * platform/mediastream/MediaConstraints.h:
+ (WebCore::MediaConstraint::fitnessDistance):
+ (WebCore::MediaConstraint::merge):
+ (WebCore::NumericConstraint::nearlyEqual):
+ (WebCore::FlattenedConstraint::isEmpty):
+ (WebCore::FlattenedConstraint::begin):
+ (WebCore::FlattenedConstraint::end):
+ (WebCore::MediaConstraint::~MediaConstraint): Deleted.
+ (WebCore::MediaConstraint::find): Deleted.
+ (WebCore::MediaConstraint::getIdeal): Deleted.
+
+ * platform/mediastream/RealtimeMediaSource.cpp:
+ (WebCore::RealtimeMediaSource::fitnessDistance): Return the fitness distance between the source
+ capabilities and a constraint.
+ (WebCore::applyNumericConstraint): New, apply a numeric constraint.
+ (WebCore::RealtimeMediaSource::applyConstraint): Use applyNumericConstraint.
+ (WebCore::RealtimeMediaSource::selectSettings): New, implement the SelectSettings algorithm
+ (WebCore::RealtimeMediaSource::applyConstraints):
+ (WebCore::RealtimeMediaSource::supportsConstraint): Deleted.
+ (WebCore::value): Deleted.
+ * platform/mediastream/RealtimeMediaSource.h:
+
</ins><span class="cx"> 2016-09-07 Mark Lam <mark.lam@apple.com>
</span><span class="cx">
</span><span class="cx"> Add CatchScope and force all exception checks to be via ThrowScope or CatchScope.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamMediaConstraintscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -37,7 +37,7 @@
</span><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><del>-RefPtr<MediaConstraint> MediaConstraint::create(const String& name)
</del><ins>+Ref<MediaConstraint> MediaConstraint::create(const String& name)
</ins><span class="cx"> {
</span><span class="cx"> MediaConstraintType constraintType = RealtimeMediaSourceSupportedConstraints::constraintFromName(name);
</span><span class="cx">
</span><span class="lines">@@ -62,6 +62,51 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+Ref<MediaConstraint> MediaConstraint::copy() const
+{
+ return MediaConstraint::create(name());
+}
+
+Ref<MediaConstraint> IntConstraint::copy() const
+{
+ auto copy = IntConstraint::create(name(), type());
+ copy->m_min = m_min;
+ copy->m_max = m_max;
+ copy->m_exact = m_exact;
+ copy->m_ideal = m_ideal;
+
+ return copy.leakRef();
+}
+
+Ref<MediaConstraint> DoubleConstraint::copy() const
+{
+ auto copy = DoubleConstraint::create(name(), type());
+ copy->m_min = m_min;
+ copy->m_max = m_max;
+ copy->m_exact = m_exact;
+ copy->m_ideal = m_ideal;
+
+ return copy.leakRef();
+}
+
+Ref<MediaConstraint> BooleanConstraint::copy() const
+{
+ auto copy = BooleanConstraint::create(name(), type());
+ copy->m_exact = m_exact;
+ copy->m_ideal = m_ideal;
+
+ return copy.leakRef();
+}
+
+Ref<MediaConstraint> StringConstraint::copy() const
+{
+ auto copy = StringConstraint::create(name(), type());
+ copy->m_exact = m_exact;
+ copy->m_ideal = m_ideal;
+
+ return copy.leakRef();
+}
+
</ins><span class="cx"> bool BooleanConstraint::getExact(bool& exact) const
</span><span class="cx"> {
</span><span class="cx"> if (!m_exact)
</span><span class="lines">@@ -136,6 +181,95 @@
</span><span class="cx"> return emptyString();
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+double StringConstraint::fitnessDistance(const String& value) const
+{
+ // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
+
+ // 1. If the constraint is not supported by the browser, the fitness distance is 0.
+ if (isEmpty())
+ return 0;
+
+ // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
+ // dictionary's value for the constraint does not satisfy the constraint, the
+ // fitness distance is positive infinity.
+ if (!m_exact.isEmpty() && m_exact.find(value) == notFound)
+ return std::numeric_limits<double>::infinity();
+
+ // 3. If no ideal value is specified, the fitness distance is 0.
+ if (m_exact.isEmpty())
+ return 0;
+
+ // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
+ // echoCancellation), the fitness distance is the result of the formula
+ // (actual == ideal) ? 0 : 1
+ return m_ideal.find(value) != notFound ? 0 : 1;
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+double StringConstraint::fitnessDistance(const Vector<String>& values) const
+{
+ if (isEmpty())
+ return 0;
+
+ double minimumDistance = std::numeric_limits<double>::infinity();
+ for (auto& value : values)
+ minimumDistance = std::min(minimumDistance, fitnessDistance(value));
+
+ return minimumDistance;
+}
+
+void StringConstraint::merge(const MediaConstraint& other)
+{
+ if (other.isEmpty())
+ return;
+
+ Vector<String> values;
+ if (other.getExact(values)) {
+ if (m_exact.isEmpty())
+ m_exact = values;
+ else {
+ for (auto& value : values) {
+ if (m_exact.find(value) == notFound)
+ m_exact.append(value);
+ }
+ }
+ }
+
+ if (other.getIdeal(values)) {
+ if (m_ideal.isEmpty())
+ m_ideal = values;
+ else {
+ for (auto& value : values) {
+ if (m_ideal.find(value) == notFound)
+ m_ideal.append(value);
+ }
+ }
+ }
+}
+
+void FlattenedConstraint::set(const MediaConstraint& constraint)
+{
+ for (auto existingConstraint : m_constraints) {
+ if (existingConstraint->type() == constraint.type()) {
+ existingConstraint = constraint.copy();
+ return;
+ }
+ }
+
+ m_constraints.append(constraint.copy());
+}
+
+void FlattenedConstraint::merge(const MediaConstraint& constraint)
+{
+ for (auto existingConstraint : m_constraints) {
+ if (existingConstraint->type() == constraint.type()) {
+ existingConstraint->merge(constraint);
+ return;
+ }
+ }
+
+ m_constraints.append(constraint.copy());
+}
+
+}
+
</ins><span class="cx"> #endif // ENABLE(MEDIA_STREAM)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamMediaConstraintsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/MediaConstraints.h (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/MediaConstraints.h        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/MediaConstraints.h        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -35,8 +35,10 @@
</span><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><span class="cx">
</span><span class="cx"> #include "RealtimeMediaSourceSupportedConstraints.h"
</span><ins>+#include <cstdlib>
</ins><span class="cx"> #include <wtf/HashMap.h>
</span><span class="cx"> #include <wtf/RefCounted.h>
</span><ins>+#include <wtf/text/StringHash.h>
</ins><span class="cx"> #include <wtf/text/WTFString.h>
</span><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -43,11 +45,13 @@
</span><span class="cx">
</span><span class="cx"> class MediaConstraint : public RefCounted<MediaConstraint> {
</span><span class="cx"> public:
</span><del>- static RefPtr<MediaConstraint> create(const String& name);
</del><ins>+ static Ref<MediaConstraint> create(const String&);
</ins><span class="cx">
</span><span class="cx"> enum class ConstraintType { ExactConstraint, IdealConstraint, MinConstraint, MaxConstraint };
</span><span class="cx">
</span><span class="cx"> virtual ~MediaConstraint() { };
</span><ins>+
+ virtual Ref<MediaConstraint> copy() const;
</ins><span class="cx"> virtual bool isEmpty() const = 0;
</span><span class="cx"> virtual bool isMandatory() const = 0;
</span><span class="cx">
</span><span class="lines">@@ -57,6 +61,7 @@
</span><span class="cx"> virtual bool getIdeal(int&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool validForRange(int, int) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual int find(std::function<bool(ConstraintType, int)>) const { ASSERT_NOT_REACHED(); return 0; }
</span><ins>+ virtual double fitnessDistance(int, int) const { ASSERT_NOT_REACHED(); return 0; }
</ins><span class="cx">
</span><span class="cx"> virtual bool getMin(double&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool getMax(double&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="lines">@@ -64,11 +69,13 @@
</span><span class="cx"> virtual bool getIdeal(double&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool validForRange(double, double) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual double find(std::function<bool(ConstraintType, double)>) const { ASSERT_NOT_REACHED(); return 0; }
</span><ins>+ virtual double fitnessDistance(double, double) const { ASSERT_NOT_REACHED(); return 0; }
</ins><span class="cx">
</span><span class="cx"> virtual bool getMin(bool&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool getMax(bool&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool getExact(bool&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool getIdeal(bool&) const { ASSERT_NOT_REACHED(); return false; }
</span><ins>+ virtual double fitnessDistance(bool) const { ASSERT_NOT_REACHED(); return 0; }
</ins><span class="cx">
</span><span class="cx"> virtual bool getMin(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual bool getMax(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="lines">@@ -76,6 +83,11 @@
</span><span class="cx"> virtual bool getIdeal(Vector<String>&) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx"> virtual const String& find(std::function<bool(ConstraintType, const String&)>) const { ASSERT_NOT_REACHED(); return emptyString(); }
</span><span class="cx">
</span><ins>+ virtual double fitnessDistance(const String&) const { ASSERT_NOT_REACHED(); return 0; }
+ virtual double fitnessDistance(const Vector<String>&) const { ASSERT_NOT_REACHED(); return 0; }
+
+ virtual void merge(const MediaConstraint&) { ASSERT_NOT_REACHED(); }
+
</ins><span class="cx"> MediaConstraintType type() const { return m_type; }
</span><span class="cx"> const String& name() const { return m_name; }
</span><span class="cx">
</span><span class="lines">@@ -134,19 +146,99 @@
</span><span class="cx"> return true;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ bool nearlyEqual(double a, double b) const
+ {
+ // Don't require strict equality when comparing constraints, or many floating point constraint values,
+ // e.g. "aspectRatio: 1.333", will never match.
+ const double epsilon = 0.00001;
+ return std::abs(a - b) <= epsilon;
+ }
+
+ double fitnessDistance(ValueType rangeMin, ValueType rangeMax) const final {
+ // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
+ // 1. If the constraint is not supported by the browser, the fitness distance is 0.
+ if (isEmpty())
+ return 0;
+
+ // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
+ // dictionary's value for the constraint does not satisfy the constraint, the
+ // fitness distance is positive infinity.
+ bool valid = validForRange(rangeMin, rangeMax);
+ if (m_exact && !valid)
+ return std::numeric_limits<double>::infinity();
+
+ if (m_min && !valid)
+ return std::numeric_limits<double>::infinity();
+
+ if (m_max && !valid)
+ return std::numeric_limits<double>::infinity();
+
+ // 3. If no ideal value is specified, the fitness distance is 0.
+ if (!m_ideal)
+ return 0;
+
+ // 4. For all positive numeric non-required constraints (such as height, width, frameRate,
+ // aspectRatio, sampleRate and sampleSize), the fitness distance is the result of the formula
+ //
+ // (actual == ideal) ? 0 : |actual - ideal| / max(|actual|,|ideal|)
+ ValueType ideal = m_ideal.value();
+ if (ideal >= rangeMin && ideal <= rangeMax)
+ return 0;
+
+ ideal = ideal > std::max(rangeMin, rangeMax) ? rangeMax : rangeMin;
+ return static_cast<double>(std::abs(ideal - m_ideal.value())) / std::max(std::abs(ideal), std::abs(m_ideal.value()));
+ }
+
+ void merge(const MediaConstraint& other) final {
+ if (other.isEmpty())
+ return;
+
+ ValueType value;
+ if (other.getExact(value))
+ m_exact = value;
+
+ if (other.getMin(value))
+ m_min = value;
+
+ if (other.getMax(value))
+ m_max = value;
+
+ // https://w3c.github.io/mediacapture-main/#constrainable-interface
+ // When processing advanced constraints:
+ // ... the User Agent must attempt to apply, individually, any 'ideal' constraints or
+ // a constraint given as a bare value for the property. Of these properties, it must
+ // satisfy the largest number that it can, in any order.
+ if (other.getIdeal(value)) {
+ if (!m_ideal || value > m_ideal.value())
+ m_ideal = value;
+ }
+ }
+
</ins><span class="cx"> bool validForRange(ValueType rangeMin, ValueType rangeMax) const final {
</span><span class="cx"> if (isEmpty())
</span><span class="cx"> return false;
</span><span class="cx">
</span><del>- if (m_exact && (m_exact.value() < rangeMin || m_exact.value() > rangeMax))
- return false;
</del><ins>+ if (m_exact) {
+ const ValueType exact = m_exact.value();
+ if (exact < rangeMin && !nearlyEqual(exact, rangeMin))
+ return false;
+ if (exact > rangeMax && !nearlyEqual(exact, rangeMax))
+ return false;
+ }
</ins><span class="cx">
</span><del>- if (m_min && m_min.value() > rangeMax)
- return false;
</del><ins>+ if (m_min) {
+ const ValueType constraintMin = m_min.value();
+ if (constraintMin > rangeMax && !nearlyEqual(constraintMin, rangeMax))
+ return false;
+ }
</ins><span class="cx">
</span><del>- if (m_max && m_max.value() < rangeMin)
- return false;
</del><span class="cx">
</span><ins>+ if (m_max) {
+ const ValueType constraintMax = m_max.value();
+ if (constraintMax < rangeMin && !nearlyEqual(constraintMax, rangeMin))
+ return false;
+ }
+
</ins><span class="cx"> return true;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -173,7 +265,6 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span><del>-private:
</del><span class="cx"> Optional<ValueType> m_min;
</span><span class="cx"> Optional<ValueType> m_max;
</span><span class="cx"> Optional<ValueType> m_exact;
</span><span class="lines">@@ -184,6 +275,8 @@
</span><span class="cx"> public:
</span><span class="cx"> static Ref<IntConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new IntConstraint(name, type)); }
</span><span class="cx">
</span><ins>+ Ref<MediaConstraint> copy() const final;
+
</ins><span class="cx"> private:
</span><span class="cx"> explicit IntConstraint(const String& name, MediaConstraintType type)
</span><span class="cx"> : NumericConstraint<int>(name, type)
</span><span class="lines">@@ -195,6 +288,8 @@
</span><span class="cx"> public:
</span><span class="cx"> static Ref<DoubleConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new DoubleConstraint(name, type)); }
</span><span class="cx">
</span><ins>+ Ref<MediaConstraint> copy() const final;
+
</ins><span class="cx"> private:
</span><span class="cx"> explicit DoubleConstraint(const String& name, MediaConstraintType type)
</span><span class="cx"> : NumericConstraint<double>(name, type)
</span><span class="lines">@@ -206,6 +301,8 @@
</span><span class="cx"> public:
</span><span class="cx"> static Ref<BooleanConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new BooleanConstraint(name, type)); }
</span><span class="cx">
</span><ins>+ Ref<MediaConstraint> copy() const final;
+
</ins><span class="cx"> void setExact(bool value) { m_exact = value; }
</span><span class="cx"> void setIdeal(bool value) { m_ideal = value; }
</span><span class="cx">
</span><span class="lines">@@ -215,6 +312,42 @@
</span><span class="cx"> bool isEmpty() const final { return !m_exact && !m_ideal; };
</span><span class="cx"> bool isMandatory() const final { return bool(m_exact); }
</span><span class="cx">
</span><ins>+ double fitnessDistance(bool value) const final {
+ // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
+ // 1. If the constraint is not supported by the browser, the fitness distance is 0.
+ if (isEmpty())
+ return 0;
+
+ // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
+ // dictionary's value for the constraint does not satisfy the constraint, the
+ // fitness distance is positive infinity.
+ if (m_exact && value != m_exact.value())
+ return std::numeric_limits<double>::infinity();
+
+ // 3. If no ideal value is specified, the fitness distance is 0.
+ if (!m_ideal || m_ideal.value() == value)
+ return 0;
+
+ // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
+ // echoCancellation), the fitness distance is the result of the formula
+ // (actual == ideal) ? 0 : 1
+ return 1;
+ }
+
+ void merge(const MediaConstraint& other) final {
+ if (other.isEmpty())
+ return;
+
+ bool value;
+ if (other.getExact(value))
+ m_exact = value;
+
+ if (other.getIdeal(value)) {
+ if (!m_ideal || (value && !m_ideal.value()))
+ m_ideal = value;
+ }
+ }
+
</ins><span class="cx"> private:
</span><span class="cx"> explicit BooleanConstraint(const String& name, MediaConstraintType type)
</span><span class="cx"> : MediaConstraint(name, type)
</span><span class="lines">@@ -229,6 +362,8 @@
</span><span class="cx"> public:
</span><span class="cx"> static Ref<StringConstraint> create(const String& name, MediaConstraintType type) { return adoptRef(*new StringConstraint(name, type)); }
</span><span class="cx">
</span><ins>+ Ref<MediaConstraint> copy() const final;
+
</ins><span class="cx"> void setExact(const String&);
</span><span class="cx"> void appendExact(const String&);
</span><span class="cx"> void setIdeal(const String&);
</span><span class="lines">@@ -240,8 +375,12 @@
</span><span class="cx"> bool isEmpty() const final { return m_exact.isEmpty() && m_ideal.isEmpty(); }
</span><span class="cx"> bool isMandatory() const final { return !m_exact.isEmpty(); }
</span><span class="cx">
</span><del>- const String& find(std::function<bool(ConstraintType, const String&)>) const override;
</del><ins>+ double fitnessDistance(const String&) const final;
+ double fitnessDistance(const Vector<String>&) const final;
</ins><span class="cx">
</span><ins>+ const String& find(std::function<bool(ConstraintType, const String&)>) const final;
+ void merge(const MediaConstraint&) final;
+
</ins><span class="cx"> private:
</span><span class="cx"> explicit StringConstraint(const String& name, MediaConstraintType type)
</span><span class="cx"> : MediaConstraint(name, type)
</span><span class="lines">@@ -268,6 +407,22 @@
</span><span class="cx">
</span><span class="cx"> using MediaTrackConstraintSetMap = HashMap<String, RefPtr<MediaConstraint>>;
</span><span class="cx">
</span><ins>+class FlattenedConstraint {
+public:
+ typedef Vector<RefPtr<MediaConstraint>>::const_iterator const_iterator;
+
+ void set(const MediaConstraint&);
+ void merge(const MediaConstraint&);
+ bool isEmpty() const { return m_constraints.isEmpty(); }
+
+ const_iterator begin() const { return m_constraints.begin(); }
+ const_iterator end() const { return m_constraints.end(); }
+
+private:
+
+ Vector<RefPtr<MediaConstraint>> m_constraints;
+};
+
</ins><span class="cx"> class MediaConstraints : public RefCounted<MediaConstraints> {
</span><span class="cx"> public:
</span><span class="cx"> virtual ~MediaConstraints() { }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamRealtimeMediaSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -150,7 +150,7 @@
</span><span class="cx"> stop(callingObserver);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-RealtimeMediaSource::ConstraintSupport RealtimeMediaSource::supportsConstraint(const MediaConstraint& constraint)
</del><ins>+double RealtimeMediaSource::fitnessDistance(const MediaConstraint& constraint)
</ins><span class="cx"> {
</span><span class="cx"> RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
</span><span class="cx">
</span><span class="lines">@@ -157,137 +157,102 @@
</span><span class="cx"> switch (constraint.type()) {
</span><span class="cx"> case MediaConstraintType::Width: {
</span><span class="cx"> if (!capabilities.supportsWidth())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- auto widthRange = capabilities.width();
- return constraint.validForRange(widthRange.rangeMin().asInt, widthRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ auto range = capabilities.width();
+ return constraint.fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::Height: {
</span><span class="cx"> if (!capabilities.supportsHeight())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- auto heightRange = capabilities.height();
- return constraint.validForRange(heightRange.rangeMin().asInt, heightRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ auto range = capabilities.height();
+ return constraint.fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::FrameRate: {
</span><span class="cx"> if (!capabilities.supportsFrameRate())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- auto rateRange = capabilities.frameRate();
- return constraint.validForRange(rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ auto range = capabilities.frameRate();
+ return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::AspectRatio: {
</span><span class="cx"> if (!capabilities.supportsAspectRatio())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><span class="cx"> auto range = capabilities.aspectRatio();
</span><del>- return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::Volume: {
</span><span class="cx"> if (!capabilities.supportsVolume())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><span class="cx"> auto range = capabilities.volume();
</span><del>- return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::SampleRate: {
</span><span class="cx"> if (!capabilities.supportsSampleRate())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><span class="cx"> auto range = capabilities.sampleRate();
</span><del>- return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::SampleSize: {
</span><span class="cx"> if (!capabilities.supportsSampleSize())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><span class="cx"> auto range = capabilities.sampleSize();
</span><del>- return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+ return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::FacingMode: {
</span><span class="cx"> if (!capabilities.supportsFacingMode())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- ConstraintSupport support = ConstraintSupport::Ignored;
- auto& supportedModes = capabilities.facingMode();
- std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [supportedModes, &support](MediaConstraint::ConstraintType type, const String& modeString) {
- if (type == MediaConstraint::ConstraintType::ExactConstraint)
- support = ConstraintSupport::Unsupported;
-
- auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
- for (auto& supportedMode : supportedModes) {
- if (supportedMode == mode) {
- support = ConstraintSupport::Supported;
- break;
- }
- }
-
- return type == MediaConstraint::ConstraintType::ExactConstraint ? true : false;
- };
-
- constraint.find(filter);
- return support;
</del><ins>+ auto& modes = capabilities.facingMode();
+ Vector<String> supportedModes;
+ supportedModes.reserveInitialCapacity(modes.size());
+ for (auto& mode : modes)
+ supportedModes.append(RealtimeMediaSourceSettings::facingMode(mode));
+ return constraint.fitnessDistance(supportedModes);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- case MediaConstraintType::EchoCancellation:
</del><ins>+ case MediaConstraintType::EchoCancellation: {
</ins><span class="cx"> if (!capabilities.supportsEchoCancellation())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- if (capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadOnly)
- return constraint.isMandatory() ? ConstraintSupport::Unsupported : ConstraintSupport::Ignored;
-
- return ConstraintSupport::Supported;
</del><ins>+ bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
+ return constraint.fitnessDistance(echoCancellationReadWrite);
</ins><span class="cx"> break;
</span><ins>+ }
</ins><span class="cx">
</span><span class="cx"> case MediaConstraintType::DeviceId: {
</span><span class="cx"> if (!capabilities.supportsDeviceId())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- ConstraintSupport support = ConstraintSupport::Ignored;
- std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [this, &support](MediaConstraint::ConstraintType type, const String& idString) {
- if (type != MediaConstraint::ConstraintType::ExactConstraint)
- return false; // Keep looking.
-
- support = idString == m_id ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
- return false;
- };
-
- constraint.find(filter);
- return support;
</del><ins>+ return constraint.fitnessDistance(m_id);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> case MediaConstraintType::GroupId: {
</span><span class="cx"> if (!capabilities.supportsDeviceId())
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx">
</span><del>- ConstraintSupport support = ConstraintSupport::Ignored;
- String groupId = settings().groupId();
- std::function<bool(MediaConstraint::ConstraintType, const String&)> filter = [groupId, &support](MediaConstraint::ConstraintType type, const String& idString) {
- if (type != MediaConstraint::ConstraintType::ExactConstraint)
- return false; // Keep looking.
-
- support = idString == groupId ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
- return false;
- };
-
- constraint.find(filter);
- return support;
</del><ins>+ return constraint.fitnessDistance(settings().groupId());
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -296,39 +261,50 @@
</span><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- return ConstraintSupport::Ignored;
</del><ins>+ return 0;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-
-template <typename T>
-T value(const MediaConstraint& constraint, T rangeMin, T rangeMax)
</del><ins>+template <typename ValueType>
+static void applyNumericConstraint(const MediaConstraint& constraint, ValueType current, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource* source, void (RealtimeMediaSource::*function)(ValueType))
</ins><span class="cx"> {
</span><del>- T result;
</del><ins>+ ValueType value;
+ ValueType min = capabilityMin;
+ ValueType max = capabilityMax;
</ins><span class="cx">
</span><del>- if (constraint.getExact(result)) {
- ASSERT(result >= rangeMin && result <= rangeMax);
- return result;
</del><ins>+ if (constraint.getExact(value)) {
+ ASSERT(constraint.validForRange(capabilityMin, capabilityMax));
+ (source->*function)(value);
+ return;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- if (constraint.getIdeal(result)) {
- if (result < rangeMin)
- result = rangeMin;
- else if (result > rangeMax)
- result = rangeMax;
</del><ins>+ if (constraint.getMin(value)) {
+ ASSERT(constraint.validForRange(value, capabilityMax));
+ if (value > min)
+ min = value;
</ins><span class="cx">
</span><del>- return result;
</del><ins>+ // If there is no ideal, don't change if minimum is smaller than current.
+ ValueType ideal;
+ if (!constraint.getIdeal(ideal) && value < current)
+ value = current;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- if (constraint.getMin(result) && result > rangeMax)
- return false;
</del><ins>+ if (constraint.getMax(value)) {
+ ASSERT(constraint.validForRange(capabilityMin, value));
+ if (value < max)
+ max = value;
+ }
</ins><span class="cx">
</span><del>- if (constraint.getMax(result) && result < rangeMin)
- return false;
-
- return result;
</del><ins>+ if (constraint.getIdeal(value)) {
+ if (value < min)
+ value = min;
+ if (value > max)
+ value = max;
+ }
+
+ if (value != current)
+ (source->*function)(value);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-
</del><span class="cx"> void RealtimeMediaSource::applyConstraint(const MediaConstraint& constraint)
</span><span class="cx"> {
</span><span class="cx"> RealtimeMediaSourceCapabilities& capabilities = *this->capabilities();
</span><span class="lines">@@ -337,8 +313,8 @@
</span><span class="cx"> if (!capabilities.supportsWidth())
</span><span class="cx"> return;
</span><span class="cx">
</span><del>- auto widthRange = capabilities.width();
- setWidth(value(constraint, widthRange.rangeMin().asInt, widthRange.rangeMax().asInt));
</del><ins>+ auto range = capabilities.width();
+ applyNumericConstraint(constraint, size().width(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setWidth);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -346,8 +322,8 @@
</span><span class="cx"> if (!capabilities.supportsHeight())
</span><span class="cx"> return;
</span><span class="cx">
</span><del>- auto heightRange = capabilities.height();
- setHeight(value(constraint, heightRange.rangeMin().asInt, heightRange.rangeMax().asInt));
</del><ins>+ auto range = capabilities.height();
+ applyNumericConstraint(constraint, size().height(), range.rangeMin().asInt, range.rangeMax().asInt, this, &RealtimeMediaSource::setHeight);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -355,8 +331,8 @@
</span><span class="cx"> if (!capabilities.supportsFrameRate())
</span><span class="cx"> return;
</span><span class="cx">
</span><del>- auto rateRange = capabilities.frameRate();
- setFrameRate(value(constraint, rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble));
</del><ins>+ auto range = capabilities.frameRate();
+ applyNumericConstraint(constraint, frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setFrameRate);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -365,7 +341,7 @@
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> auto range = capabilities.aspectRatio();
</span><del>- setAspectRatio(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+ applyNumericConstraint(constraint, aspectRatio(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setAspectRatio);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -374,8 +350,7 @@
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> auto range = capabilities.volume();
</span><del>- // std::pair<T, T> valuesForRange(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)
- setVolume(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+ applyNumericConstraint(constraint, volume(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setVolume);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -384,7 +359,7 @@
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> auto range = capabilities.sampleRate();
</span><del>- setSampleRate(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+ applyNumericConstraint(constraint, sampleRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setSampleRate);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -393,7 +368,7 @@
</span><span class="cx"> return;
</span><span class="cx">
</span><span class="cx"> auto range = capabilities.sampleSize();
</span><del>- setSampleSize(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+ applyNumericConstraint(constraint, sampleSize(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &RealtimeMediaSource::setSampleSize);
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -436,22 +411,101 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
</del><ins>+bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
</ins><span class="cx"> {
</span><del>- ASSERT(constraints.isValid());
</del><ins>+ // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
+ //
+ // 1. Each constraint specifies one or more values (or a range of values) for its property.
+ // A property may appear more than once in the list of 'advanced' ConstraintSets. If an
+ // empty object or list has been given as the value for a constraint, it must be interpreted
+ // as if the constraint were not specified (in other words, an empty constraint == no constraint).
+ //
+ // Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
+ // constraints will silently disappear. To avoid this being a surprise, application authors are
+ // expected to first use the getSupportedConstraints() method as shown in the Examples below.
</ins><span class="cx">
</span><ins>+ // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
+ // unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
+ // removed.)
+
+ // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
+ // properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
+ // distance is finite.
</ins><span class="cx"> auto& mandatoryConstraints = constraints.mandatoryConstraints();
</span><span class="cx"> for (auto& nameConstraintPair : mandatoryConstraints) {
</span><del>- auto& constraint = *nameConstraintPair.value;
- if (supportsConstraint(constraint) == ConstraintSupport::Unsupported) {
- failureHandler(constraint.name(), "Constraint not supported");
- return;
</del><ins>+ const auto& constraint = nameConstraintPair.value;
+ if (fitnessDistance(*constraint) == std::numeric_limits<double>::infinity()) {
+ failedConstraint = constraint->name();
+ return false;
</ins><span class="cx"> }
</span><ins>+
+ candidates.set(*constraint);
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- for (auto& nameConstraintPair : mandatoryConstraints)
- applyConstraint(*nameConstraintPair.value);
</del><ins>+ // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
+ if (candidates.isEmpty())
+ return true;
</ins><span class="cx">
</span><ins>+ // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
+ // For each ConstraintSet:
+
+ // 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;
+ double minimumDistance = std::numeric_limits<double>::infinity();
+ for (const auto& advancedConstraint : constraints.advancedConstraints()) {
+ double constraintDistance = 0;
+ bool supported = false;
+ for (auto& nameConstraintPair : advancedConstraint) {
+ double distance = fitnessDistance(*nameConstraintPair.value);
+ constraintDistance += distance;
+ if (distance != std::numeric_limits<double>::infinity())
+ supported = true;
+ }
+
+ if (constraintDistance < minimumDistance)
+ minimumDistance = 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.
+ // If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
+ if (supported)
+ supportedConstraints.append({constraintDistance, advancedConstraint});
+ }
+
+ // 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 (minimumDistance != std::numeric_limits<double>::infinity()) {
+ supportedConstraints.removeAllMatching( [&] (std::pair<double, MediaTrackConstraintSetMap> pair) -> bool {
+ return pair.first > minimumDistance;
+ });
+
+ if (!supportedConstraints.isEmpty()) {
+ auto& advancedConstraint = supportedConstraints[0].second;
+ for (auto& nameConstraintPair : advancedConstraint)
+ candidates.merge(*nameConstraintPair.value);
+ }
+ }
+
+ return true;
+}
+
+
+void RealtimeMediaSource::applyConstraints(const MediaConstraints& constraints, SuccessHandler successHandler, FailureHandler failureHandler)
+{
+ ASSERT(constraints.isValid());
+
+ FlattenedConstraint candidates;
+
+ String failedConstraint;
+ if (!selectSettings(constraints, candidates, failedConstraint)) {
+ failureHandler(failedConstraint, "Constraint not supported");
+ return;
+ }
+
+ for (auto constraint : candidates)
+ applyConstraint(*constraint);
+
</ins><span class="cx"> successHandler();
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamRealtimeMediaSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -42,7 +42,6 @@
</span><span class="cx"> #include "MediaSample.h"
</span><span class="cx"> #include "PlatformLayer.h"
</span><span class="cx"> #include "RealtimeMediaSourceCapabilities.h"
</span><del>-#include <wtf/Lock.h>
</del><span class="cx"> #include <wtf/RefCounted.h>
</span><span class="cx"> #include <wtf/Vector.h>
</span><span class="cx"> #include <wtf/WeakPtr.h>
</span><span class="lines">@@ -171,13 +170,12 @@
</span><span class="cx"> private:
</span><span class="cx"> WeakPtr<RealtimeMediaSource> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
</span><span class="cx">
</span><del>- enum ConstraintSupport { Ignored, Supported, Unsupported };
- ConstraintSupport supportsConstraint(const MediaConstraint&);
</del><ins>+ bool supportsConstraints(const MediaTrackConstraintSetMap&);
+ bool selectSettings(const MediaConstraints&, FlattenedConstraint&, String&);
+ double fitnessDistance(const MediaConstraint&);
</ins><span class="cx"> void applyConstraint(const MediaConstraint&);
</span><span class="cx">
</span><span class="cx"> WeakPtrFactory<RealtimeMediaSource> m_weakPtrFactory;
</span><del>- Lock m_lock;
-
</del><span class="cx"> String m_id;
</span><span class="cx"> String m_persistentID;
</span><span class="cx"> Type m_type;
</span></span></pre>
</div>
</div>
</body>
</html>