Skip to content

Commit

Permalink
Use required Image Capture constraints to overconstrain
Browse files Browse the repository at this point in the history
Advanced constraints sets passed to the MediaStreamTrack
applyConstraints method should either be applied or discarded
(SelectSettings algorithm[1] step 5.2). They should
never cause an OverconstrainedError to be thrown. This CL fixed Image
Capture tests to use required constraints (such as
  videoTrack.applyConstraints({brightness: {exact: 3}})
) instead of advanced constrained (such as
  videoTrack.applyConstraints({advanced: [{brightness: 3}]})
) to throw an OverconstrainedError.

[1]: https://w3c.github.io/mediacapture-main/#dfn-selectsettings

Previously Chrome did not support required Image Capture constraints and
does treat advanced Image Capture constraints in the first advanced
constraint set as if they were required. Therefore Image Capture test
were written using advanced constraints instead of using required
constraints. This CL fixes that.

Bug: 1408091
Change-Id: Ib23348c81ec73fd8f2594baabf3433cfefadcd06
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4651034
Commit-Queue: Eero Hakkinen <[email protected]>
Reviewed-by: Rijubrata Bhaumik <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1163773}
  • Loading branch information
eehakkin authored and chromium-wpt-export-bot committed Jun 28, 2023
1 parent 3d67ca9 commit dcd3bae
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 42 deletions.
20 changes: 10 additions & 10 deletions mediacapture-image/MediaStreamTrack-applyConstraints-fast.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@
// when called with an empty advanced constraint set, returns a Promise that is
// resolved.
promise_test(function(t) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
context.fillStyle = "red";
context.fillRect(0, 0, 10, 10);
var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
context.fillStyle = "red";
context.fillRect(0, 0, 10, 10);

var stream = canvas.captureStream();
assert_equals(stream.getAudioTracks().length, 0);
assert_equals(stream.getVideoTracks().length, 1);
var stream = canvas.captureStream();
assert_equals(stream.getAudioTracks().length, 0);
assert_equals(stream.getVideoTracks().length, 1);

var videoTrack = stream.getVideoTracks()[0];
return videoTrack.applyConstraints({advanced: []});
var videoTrack = stream.getVideoTracks()[0];
return videoTrack.applyConstraints({advanced: []});
}, 'MediaStreamTrack.applyConstraints({advanced: []})');

// This test verifies that applyConstraints() rejects the returned Promise if
Expand All @@ -55,7 +55,7 @@
// Use e.g. |torch| as an example of unsupported constraint.
assert_false("torch" in videoTrack.getCapabilities());
try {
await videoTrack.applyConstraints({advanced : [ {torch : true} ]});
await videoTrack.applyConstraints({torch: {exact: true}});
} catch (error) {
assert_equals(error.name, 'OverconstrainedError');
assert_equals(error.constraint, 'torch');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,20 +101,61 @@
let stream = await navigator.mediaDevices.getUserMedia({video: true});
let videoTrack = stream.getVideoTracks()[0];

const constraints = [{ pan: 8 }, { tilt: 9 }];
const constraints = [{ pan: { exact: 8 } }, { tilt: { exact: 9 } }];
await Promise.all(constraints.map(async constraint => {
try {
await videoTrack.applyConstraints({ advanced: [constraint] });
await videoTrack.applyConstraints(constraint);
} catch (error) {
assert_equals(error.name, 'OverconstrainedError');
assert_equals(error.constraint, Object.keys(constraint)[0]);
return;
}
assert_unreached(
"applyConstraints should throw a NotSupportedError for " +
"applyConstraints should throw an OverconstrainedError for " +
JSON.stringify(constraint));
}));

}, 'exercises an applyConstraints() with PTZ permission denied');
}, 'exercises an applyConstraints() with required constraints with PTZ permission denied');

image_capture_test(async t => {
await test_driver.set_permission({name: 'camera', panTiltZoom: true},
'denied');

let stream = await navigator.mediaDevices.getUserMedia({video: true});
let videoTrack = stream.getVideoTracks()[0];

const constraints = [{ pan: { ideal: 8 } }, { tilt: { ideal: 9 } }];
await Promise.all(constraints.map(async constraint => {
try {
await videoTrack.applyConstraints(constraint);
} catch (error) {
assert_unreached(
`applyConstraints should not throw an ${error.name} for ` +
JSON.stringify(constraint));
}
}));

}, 'exercises an applyConstraints() with ideal constraints with PTZ permission denied');

image_capture_test(async t => {
await test_driver.set_permission({name: 'camera', panTiltZoom: true},
'denied');

let stream = await navigator.mediaDevices.getUserMedia({video: true});
let videoTrack = stream.getVideoTracks()[0];

const advanced_constraints = [{ pan: 8 }, { tilt: 9 }];
await Promise.all(advanced_constraints.map(async advanced_constraint => {
const constraint = { advanced: [advanced_constraint] };
try {
await videoTrack.applyConstraints(constraint);
} catch (error) {
assert_unreached(
`applyConstraints should not throw an ${error.name} for ` +
JSON.stringify(constraint));
}
}));

}, 'exercises an applyConstraints() with advances constraints with PTZ permission denied');

</script>
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@

const constraint = getConstraint(imageCaptureTest.mockImageCapture().state());
try {
const constraints = { advanced : [constraint] };
await videoTrack.applyConstraints(constraints);
await videoTrack.applyConstraints(constraint);
assert_unreached('expected applyConstraints to reject');
} catch (error) {
assert_equals(error.name, 'OverconstrainedError');
Expand All @@ -31,38 +30,38 @@
};

const constraintGenerators = [
capabilities => ({ whiteBalanceMode: 'manual' }),
capabilities => ({ exposureMode: 'none' }),
capabilities => ({ focusMode: 'continuous' }),
capabilities => ({ whiteBalanceMode: { exact: 'manual' } }),
capabilities => ({ exposureMode: { exact: 'none' } }),
capabilities => ({ focusMode: { exact: 'continuous' } }),
capabilities => ({
exposureCompensation: capabilities.exposureCompensation.max + 1
exposureCompensation: { exact: capabilities.exposureCompensation.max + 1 }
}),
capabilities => ({
exposureCompensation: capabilities.exposureCompensation.min - 1
exposureCompensation: { exact: capabilities.exposureCompensation.min - 1 }
}),
capabilities => ({
colorTemperature: capabilities.colorTemperature.max + 1
colorTemperature: { exact: capabilities.colorTemperature.max + 1 }
}),
capabilities => ({
colorTemperature: capabilities.colorTemperature.min - 1
colorTemperature: { exact: capabilities.colorTemperature.min - 1 }
}),
capabilities => ({ iso: capabilities.iso.max + 1 }),
capabilities => ({ iso: capabilities.iso.min - 1 }),
capabilities => ({ brightness: capabilities.brightness.max + 1 }),
capabilities => ({ brightness: capabilities.brightness.min - 1 }),
capabilities => ({ contrast: capabilities.contrast.max + 1 }),
capabilities => ({ contrast: capabilities.contrast.min - 1 }),
capabilities => ({ saturation: capabilities.saturation.max + 1 }),
capabilities => ({ saturation: capabilities.saturation.min - 1 }),
capabilities => ({ sharpness: capabilities.sharpness.max + 1 }),
capabilities => ({ sharpness: capabilities.sharpness.min - 1 }),
capabilities => ({ pan: capabilities.pan.max + 1 }),
capabilities => ({ pan: capabilities.pan.min - 1 }),
capabilities => ({ tilt: capabilities.tilt.max + 1 }),
capabilities => ({ tilt: capabilities.tilt.min - 1 }),
capabilities => ({ zoom: capabilities.zoom.max + 1 }),
capabilities => ({ zoom: capabilities.zoom.min - 1 }),
capabilities => ({ torch: true }),
capabilities => ({ iso: { exact: capabilities.iso.max + 1 } }),
capabilities => ({ iso: { exact: capabilities.iso.min - 1 } }),
capabilities => ({ brightness: { exact: capabilities.brightness.max + 1 } }),
capabilities => ({ brightness: { exact: capabilities.brightness.min - 1 } }),
capabilities => ({ contrast: { exact: capabilities.contrast.max + 1 } }),
capabilities => ({ contrast: { exact: capabilities.contrast.min - 1 } }),
capabilities => ({ saturation: { exact: capabilities.saturation.max + 1 } }),
capabilities => ({ saturation: { exact: capabilities.saturation.min - 1 } }),
capabilities => ({ sharpness: { exact: capabilities.sharpness.max + 1 } }),
capabilities => ({ sharpness: { exact: capabilities.sharpness.min - 1 } }),
capabilities => ({ pan: { exact: capabilities.pan.max + 1 } }),
capabilities => ({ pan: { exact: capabilities.pan.min - 1 } }),
capabilities => ({ tilt: { exact: capabilities.tilt.max + 1 } }),
capabilities => ({ tilt: { exact: capabilities.tilt.min - 1 } }),
capabilities => ({ zoom: { exact: capabilities.zoom.max + 1 } }),
capabilities => ({ zoom: { exact: capabilities.zoom.min - 1 } }),
capabilities => ({ torch: { exact: true } }),
];

for (key in constraintGenerators) {
Expand Down
13 changes: 11 additions & 2 deletions mediacapture-image/MediaStreamTrack-clone.https.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@

torch : true
}]};
for (const [key, value] of Object.entries(constraints.advanced[0])) {
constraints[key] = {exact: value};
}

let stream = await navigator.mediaDevices.getUserMedia({video: true});
let originalVideoTrack = stream.getVideoTracks()[0];
Expand All @@ -51,14 +54,20 @@
assert_equals(appliedClonedConstraints.advanced.length, 1);
const appliedClonedAdvancedConstraints = appliedClonedConstraints.advanced[0];

// Check that |appliedClonedAdvancedConstraints| and |appliedAdvancedConstraints| are equal.
// Check that |appliedClonedConstraints| and |appliedConstraints| are equal.
const appliedAdvancedConstraints = appliedConstraints.advanced[0];
assert_equals(appliedAdvancedConstraints.length, appliedClonedAdvancedConstraints.length);
Object.keys(appliedClonedAdvancedConstraints).forEach((key, value) => {
Object.keys(appliedClonedAdvancedConstraints).forEach(key => {
assert_not_equals(appliedClonedConstraints[key], undefined, 'key ' + key);
assert_not_equals(appliedConstraints[key], undefined, 'key ' + key);
assert_not_equals(appliedConstraints[key].exact, undefined, 'key ' + key);
assert_not_equals(appliedAdvancedConstraints[key], undefined, 'key ' + key);
if (key != 'pointsOfInterest') {
assert_equals(appliedConstraints[key].exact, appliedClonedConstraints[key].exact, key);
assert_equals(appliedAdvancedConstraints[key], appliedClonedAdvancedConstraints[key], key);
} else {
assert_point2d_array_approx_equals(appliedConstraints[key].exact,
appliedClonedConstraints[key].exact, 0.01);
assert_point2d_array_approx_equals(appliedAdvancedConstraints[key],
appliedClonedAdvancedConstraints[key], 0.01);
}
Expand Down

0 comments on commit dcd3bae

Please sign in to comment.