blob: 651deeebe9f513277fae1e6b89856d105f3e2f46 [file] [log] [blame]
<!DOCTYPE HTML>
<html>
<head>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<script>
// If a constraint is set with applyConstraints, it should come back in
// getConstraints().
promise_test(() => {
return navigator.mediaDevices.getUserMedia({video: {width: {exact: 800}}})
.then(s => {
var track = s.getVideoTracks()[0];
var constraints = track.getConstraints();
assert_equals(Object.keys(constraints).length, 1);
assert_true(constraints.hasOwnProperty('width'));
assert_equals(constraints.width.exact, 800);
return track.applyConstraints({width: {exact: 640}})
.then(() => {
constraints = track.getConstraints();
assert_equals(Object.keys(constraints).length, 1);
assert_true(constraints.hasOwnProperty('width'));
assert_equals(constraints.width.exact, 640);
})
});
}, 'applyConstraints() sets the value of a constraint set by getUserMedia()');
// The deviceId constraint must be rejected, since the source of a track cannot
// be changed with applyConstraints.
promise_test(test => {
return promise_rejects_js(test,
OverconstrainedError,
navigator.mediaDevices.getUserMedia({audio: true})
.then(s => {
return s.getAudioTracks()[0].applyConstraints(
{ deviceId: {exact: 'mydevice-id'}});
}));
}, 'Attempting to change the deviceId with applyConstraints() fails');
promise_test(() => {
let track;
return navigator.mediaDevices.getUserMedia({video: true})
.then(s => {
track = s.getVideoTracks()[0];
return track.getSettings();
}).then(settings => {
return track.applyConstraints(
{ width: { exact: settings.width }, notKnownName: { exact: true }})
}).then(() => {
constraints = track.getConstraints();
assert_equals(Object.keys(constraints).length, 1);
assert_false(constraints.hasOwnProperty('notKnownName'));
});
}, 'An unsupported constraint is ignored by applyConstraints()');
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(() => {
// Construct a constraint set that covers constraints that make sense for
// video.
const complexConstraintSet = {
width: { min: 30, max: 480 },
height: { min: 30, max: 480, exact: 350 },
};
// These constraints are syntactically valid, but may cause rejection.
// They are included in an "advanced" constraint.
// The particular values chosen are picked to exercise multiple parser paths,
// but the most important is that all legal names are represented.
const ignorableConstraintSet = {
frameRate: { ideal: 30.0 },
facingMode: { ideal: "user" },
aspectRatio: { ideal: 1.3333333, exact: 1.4444 },
sampleRate: { ideal: 42, min: 31, max: 54 },
sampleSize: 3,
echoCancellation: { ideal: false, exact: true },
autoGainControl: { ideal: false, exact: true },
noiseSuppression: { ideal: false, exact: true },
latency: 0.22,
channelCount: 2,
deviceId: { ideal: ["foo", "fooz"] },
groupId: ["bar", "baz"]
};
let complexConstraints = complexConstraintSet;
complexConstraints.advanced = [ ignorableConstraintSet ];
return navigator.mediaDevices.getUserMedia({video: true})
.then(s => {
var track = s.getVideoTracks()[0];
return track.applyConstraints(complexConstraints).then(() => {
constraints = track.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(() => {
return navigator.mediaDevices.getUserMedia(
{'video': true})
.then(s => {
var track = s.getVideoTracks()[0];
track.applyConstraints({ 'advanced': [ constraints ]}).then(() => {
var constraints_out = track.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 String', { facingMode: "user1" });
constraintSyntaxTest('Ideal String', { facingMode: { ideal: "user2" }});
constraintSyntaxTest('Multiple String in Brackets',
{ facingMode: { ideal: ["user3", "left3"]}});
constraintSyntaxTest('Multiple Bracketed Naked String',
{ facingMode: ["user4", "left4"] });
constraintSyntaxTestWithChange(
'Single Bracketed string unwrapped',
{ 'facingMode': ["user5"]}, { facingMode: "user5" });
constraintSyntaxTest('Both Ideal and Exact string',
{ facingMode: { ideal: "user6", exact: "left6" }});
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 });
promise_test(async t => {
let stream = await navigator.mediaDevices.getUserMedia(
{video: {width: 639, height: 479}});
let track = stream.getVideoTracks()[0];
t.add_cleanup(()=>track.stop());
let settings = track.getSettings();
assert_equals(settings.width, 639);
assert_equals(settings.height, 479);
assert_equals(settings.resizeMode, "crop-and-scale");
await track.applyConstraints({resizeMode: {exact: "none"}});
settings = track.getSettings();
assert_equals(settings.width, 640);
assert_equals(settings.height, 480);
assert_equals(settings.resizeMode, "none");
try {
await track.applyConstraints(
{width: {exact: 639}, resizeMode: {exact: 'none'}});
t.step(()=>assert_unreached('applyConstraints should not have succeeded'));
} catch(e) {
assert_equals(e.name, 'OverconstrainedError');
assert_equals(e.constraint, 'width');
}
}, "applyConstraints() supports resizeMode in getUserMedia() tracks");
function constraintSyntaxTestContradictingProperties(
name, constraintName, constraints, contradictingConstraints) {
promise_test(async t => {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const tracks = stream.getTracks();
assert_equals(tracks.length, 1);
const track = tracks[0];
t.add_cleanup(()=>track.stop());
try {
await track.applyConstraints(contradictingConstraints);
} catch(e) {
assert_equals(e.name, 'OverconstrainedError');
assert_equals(e.constraint, constraintName);
return;
}
assert_unreached('applyConstraints should not have succeeded');
}, name);
}
constraintSyntaxTestContradictingProperties(
'Contradicting echoCancellation constraints', 'echoCancellation',
{audio: {echoCancellation: {exact: true}}},
{echoCancellation: {exact: false}});
constraintSyntaxTestContradictingProperties(
'Contradicting noiseSuppression constraints', 'noiseSuppression',
{audio: {noiseSuppression: {exact: true}}},
{noiseSuppression: {exact: false}});
constraintSyntaxTestContradictingProperties(
'Contradicting autoGainControl constraints', 'autoGainControl',
{audio: {autoGainControl: {exact: true}}},
{autoGainControl: {exact: false}});
</script>
</body>
</html>