<!DOCTYPE HTML>
<html>
<head>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
// If a constraint is specified, it should come back in getConstraints().
promise_test(function() {
return navigator.mediaDevices.getUserMedia({audio: { echoCancellation: { exact: true}, autoGainControl: { exact: true }, noiseSuppression: { exact: true }, voiceIsolation: { exact: true }}})
.then(function(s) {
constraints = s.getAudioTracks()[0].getConstraints();
assert_equals(Object.keys(constraints).length, 4);
assert_true(constraints.hasOwnProperty('echoCancellation'));
assert_true(constraints.echoCancellation.exact);
assert_true(constraints.hasOwnProperty('autoGainControl'));
assert_true(constraints.autoGainControl.exact);
assert_true(constraints.hasOwnProperty('noiseSuppression'));
assert_true(constraints.noiseSuppression.exact);
assert_true(constraints.hasOwnProperty('voiceIsolation'));
assert_true(constraints.voiceIsolation.exact);
});
}, 'A set constraint is returned on getConstraints');
promise_test(function() {
return navigator.mediaDevices.getUserMedia({audio: { echoCancellation: { exact: true}, notKnownName: { exact: true }}})
.then(function(s) {
constraints = s.getAudioTracks()[0].getConstraints();
assert_equals(Object.keys(constraints).length, 1);
assert_false(constraints.hasOwnProperty('notKnownName'));
});
}, 'An unknown constraint is NOT returned on getConstraints');
function constraintElementsEqual(a, b) {
if (a === b)
return true;
if (!(a instanceof Object))
return false;
if (!(b instanceof Object))
return false;
if (Object.keys(a).length != Object.keys(b).length)
return false;
for (var p in a) {
if (!a.hasOwnProperty(p))
continue; // Skip prototypes and such things.
if (!b.hasOwnProperty(p))
return false;
if (a[p] instanceof Object && b[p] instanceof Object) {
if (!constraintElementsEqual(a[p], b[p]))
return false;
continue;
}
if (a[p] !== b[p]) return false; // Simple types.
}
return true;
}
promise_test(function() {
// We construct a constraint set that covers all defined constraints.
// All these constraints make sense for video.
const complexConstraintSet = {
width: { min: 30, max: 480 },
height: { min: 30, max: 480, exact: 350 },
aspectRatio: 1.3333333,
frameRate: { min: 0 },
facingMode: "user"
};
// These constraints are syntactically valid, but may cause rejection.
// They are included in an "advanced" constraint.
const ignorableConstraintSet = {
sampleRate: { ideal: 42 },
sampleSize: { ideal: 3 },
echoCancellation: { ideal: false },
autoGainControl: { ideal: false },
noiseSuppression: { ideal: false },
voiceIsolation: { ideal: false },
latency: { ideal: 0.22 },
channelCount: { ideal: 2 },
deviceId: { ideal: ["foo", "fooz"] },
groupId: { ideal: ["bar", "baz"] }
};
let complexConstraints = complexConstraintSet;
complexConstraints.advanced = [ ignorableConstraintSet ];
return navigator.mediaDevices.getUserMedia({video: complexConstraints})
.then(function(s) {
constraints = s.getVideoTracks()[0].getConstraints();
assert_true(constraintElementsEqual(constraints, complexConstraints),
"Unexpected result: In: " + JSON.stringify(complexConstraints, null, 2) +
" Out: " + JSON.stringify(constraints, null, 2));
});
}, 'All valid keys are returned for complex constraints');
// Syntax tests for constraints.
// These work by putting the constraints into an advanced constraint
// (so that they can be ignored), calling getUserMedia, and then
// inspecting the constraints.
// In advanced constraints, naked values mean "exact", and "exact" values
// are thus unwrapped, which is the opposite behavior from the "basic"
// constraint set (outside advanced).
function constraintSyntaxTestWithChange(name, constraints, expected_result) {
promise_test(function() {
return navigator.mediaDevices.getUserMedia(
{'video': { 'advanced': [ constraints ]}})
.then(function(s) {
var constraints_out = s.getVideoTracks()[0].getConstraints().advanced[0];
assert_true(constraintElementsEqual(expected_result, constraints_out),
"Unexpected result: Expected: " +
JSON.stringify(expected_result, null, 2) +
" Out: " + JSON.stringify(constraints_out, null, 2));
})
}, name);
}
function constraintSyntaxTest(name, constraints) {
constraintSyntaxTestWithChange(name, constraints, constraints);
}
constraintSyntaxTest('Simple integer', { height: 42 });
constraintSyntaxTest('Ideal integer', { height: { ideal: 42 }});
constraintSyntaxTest('Min/max integer', { height: { min: 42, max: 43 }});
constraintSyntaxTestWithChange('Exact unwrapped integer',
{ height: { exact: 42 } }, { height: 42 });
constraintSyntaxTest('Simple double', { aspectRatio: 1.5 });
constraintSyntaxTest('Ideal double', { aspectRatio: { ideal: 1.5 }});
constraintSyntaxTest('Min/max double', { aspectRatio: { min: 1.5, max: 2.0 }});
constraintSyntaxTestWithChange('Exact unwrapped double',
{ aspectRatio: { exact: 1.5 } }, { aspectRatio: 1.5 });
constraintSyntaxTest('Simple facingMode string', { facingMode: "user1" });
constraintSyntaxTest('Ideal facingMode string', { facingMode: { ideal: "user2" }});
constraintSyntaxTest('Multiple facingMode string in brackets', { facingMode: { ideal: ["user3", "left3"]}});
constraintSyntaxTest('Multiple bracketed naked facingMode string', { facingMode: ["user4", "left4"] });
constraintSyntaxTestWithChange('Single bracketed facingMode string unwrapped',
{ 'facingMode': ["user5"]}, { facingMode: "user5" });
constraintSyntaxTest('Both ideal and exact facingMode string', { facingMode: { ideal: "user6", exact: "left6" }});
constraintSyntaxTest('Simple resizeMode string', { resizeMode: "none1" });
constraintSyntaxTest('Ideal resizeMode string', { resizeMode: { ideal: "none2" }});
constraintSyntaxTest('Multiple resizeMode string in brackets', { resizeMode: { ideal: ["none3", "crop3"]}});
constraintSyntaxTest('Multiple bracketed naked resizeMode string', { resizeMode: ["none4", "crop4"] });
constraintSyntaxTestWithChange('Single bracketed resizeMode string unwrapped',
{ 'resizeMode': ["none5"]}, { resizeMode: "none5" });
constraintSyntaxTest('Both ideal and exact resizeMode string', { resizeMode: { ideal: "none6", exact: "crop6" }});
constraintSyntaxTest('echoCancellation with simple boolean value', { echoCancellation: true });
constraintSyntaxTest('echoCancellation with ideal boolean value', { echoCancellation: { ideal: true }});
constraintSyntaxTestWithChange('echoCancellation with exact unwrapped boolean value',
{ echoCancellation: { exact: true } }, { echoCancellation: true });
constraintSyntaxTest('autoGainControl with simple boolean value', { autoGainControl: true });
constraintSyntaxTest('autoGainControl with ideal boolean value', { autoGainControl: { ideal: true }});
constraintSyntaxTestWithChange('autoGainControl with exact unwrapped boolean value',
{ autoGainControl: { exact: true } }, { autoGainControl: true });
constraintSyntaxTest('noiseSuppression with simple boolean value', { noiseSuppression: true });
constraintSyntaxTest('noiseSuppression with ideal boolean value', { noiseSuppression: { ideal: true }});
constraintSyntaxTestWithChange('noiseSuppression with exact unwrapped boolean value',
{ noiseSuppression: { exact: true } }, { noiseSuppression: true });
constraintSyntaxTest('voiceIsolation with simple boolean value', { voiceIsolation: true });
constraintSyntaxTest('voiceIsolation with ideal boolean value', { voiceIsolation: { ideal: true }});
constraintSyntaxTestWithChange('voiceIsolation with exact unwrapped boolean value',
{ voiceIsolation: { exact: true } }, { voiceIsolation: true });
</script>
</body>
</html>