From aeb09189862229bae8985aa6e835a62c039e658a Mon Sep 17 00:00:00 2001 From: Rijubrata Bhaumik Date: Wed, 20 Mar 2019 02:48:48 -0700 Subject: [PATCH] [ImageCapture] Add pan/tilt constraint and wire in Linux/CrOS. Pan and Tilt Constraints are part of the UVC spec. For Linux/CrOS, we can use V4L2 controls like (V4L2_CID_PAN_ABSOLUTE and V4L2_CID_TILT_ABSOLUTE). Spec: https://github.com/w3c/mediacapture-image/pull/182 Test Page: https://riju.github.io/WebCamera/samples/panTilt/ Bug:934063 Change-Id: I552c4c8be717c3b67c4d91f826a1f16850430fa4 --- ...-MediaTrackSupportedConstraints.https.html | 2 + ...eamTrack-applyConstraints-getSettings.html | 5 + .../MediaStreamTrack-applyConstraints.html | 6 ++ .../MediaStreamTrack-getCapabilities.html | 10 ++ .../MediaStreamTrack-getConstraints-fast.html | 3 + .../MediaStreamTrack-getSettings.html | 3 + resources/chromium/image_capture.mojom.js | 92 +++++++++++++------ resources/chromium/mock-imagecapture.js | 18 ++++ 8 files changed, 113 insertions(+), 26 deletions(-) diff --git a/mediacapture-image/ImageCapture-MediaTrackSupportedConstraints.https.html b/mediacapture-image/ImageCapture-MediaTrackSupportedConstraints.https.html index 9ec5261b9dc48e..fddeb60d0c24d9 100644 --- a/mediacapture-image/ImageCapture-MediaTrackSupportedConstraints.https.html +++ b/mediacapture-image/ImageCapture-MediaTrackSupportedConstraints.https.html @@ -20,6 +20,8 @@ assert_true(supported_constraints.saturation); assert_true(supported_constraints.sharpness); assert_true(supported_constraints.focusDistance); + assert_true(supported_constraints.pan); + assert_true(supported_constraints.tilt); assert_true(supported_constraints.zoom); assert_true(supported_constraints.torch); }, 'Image Capture supported constraints'); diff --git a/mediacapture-image/MediaStreamTrack-applyConstraints-getSettings.html b/mediacapture-image/MediaStreamTrack-applyConstraints-getSettings.html index 4900336549f4a9..9d985de66de4a4 100644 --- a/mediacapture-image/MediaStreamTrack-applyConstraints-getSettings.html +++ b/mediacapture-image/MediaStreamTrack-applyConstraints-getSettings.html @@ -35,6 +35,8 @@ sharpness : 6, focusDistance : 7, + pan : 8, + tilt : 9, zoom : 3.141592, torch : true @@ -89,6 +91,9 @@ assert_equals(constraints.advanced[0].focusDistance, settings.focusDistance, 'focusDistance'); + + assert_equals(constraints.advanced[0].pan, settings.pan, 'pan'); + assert_equals(constraints.advanced[0].tilt, settings.tilt, 'tilt'); assert_equals(constraints.advanced[0].zoom, settings.zoom, 'zoom'); assert_equals(constraints.advanced[0].torch, settings.torch, 'torch'); diff --git a/mediacapture-image/MediaStreamTrack-applyConstraints.html b/mediacapture-image/MediaStreamTrack-applyConstraints.html index c87f954c682cb6..a0fc28191b56d0 100644 --- a/mediacapture-image/MediaStreamTrack-applyConstraints.html +++ b/mediacapture-image/MediaStreamTrack-applyConstraints.html @@ -36,6 +36,8 @@ sharpness : 6, focusDistance : 7, + pan : 8, + tilt : 9, zoom : 3.141592, torch : true @@ -109,6 +111,10 @@ assert_equals(constraintsDict.focusDistance, theMock.options().focusDistance ,'focusDistance'); + assert_equals(constraintsDict.pan, theMock.options().pan, + 'pan'); + assert_equals(constraintsDict.tilt, theMock.options().tilt, + 'tilt'); assert_equals(constraintsDict.torch, theMock.options().torch, 'torch'); diff --git a/mediacapture-image/MediaStreamTrack-getCapabilities.html b/mediacapture-image/MediaStreamTrack-getCapabilities.html index 9e34f2389cc4e4..e7b196f510a0ba 100644 --- a/mediacapture-image/MediaStreamTrack-getCapabilities.html +++ b/mediacapture-image/MediaStreamTrack-getCapabilities.html @@ -141,6 +141,16 @@ assert_equals(capabilities.focusDistance.step, mockCapabilities.focusDistance.step); + assert_true(capabilities.pan instanceof MediaSettingsRange); + assert_equals(capabilities.pan.max, mockCapabilities.pan.max); + assert_equals(capabilities.pan.min, mockCapabilities.pan.min); + assert_equals(capabilities.pan.step, mockCapabilities.pan.step); + + assert_true(capabilities.tilt instanceof MediaSettingsRange); + assert_equals(capabilities.tilt.max, mockCapabilities.tilt.max); + assert_equals(capabilities.tilt.min, mockCapabilities.tilt.min); + assert_equals(capabilities.tilt.step, mockCapabilities.tilt.step); + assert_true(capabilities.zoom instanceof MediaSettingsRange); assert_equals(capabilities.zoom.max, mockCapabilities.zoom.max); assert_equals(capabilities.zoom.min, mockCapabilities.zoom.min); diff --git a/mediacapture-image/MediaStreamTrack-getConstraints-fast.html b/mediacapture-image/MediaStreamTrack-getConstraints-fast.html index 16f869cf119faa..3b1e2e0f2ca51e 100644 --- a/mediacapture-image/MediaStreamTrack-getConstraints-fast.html +++ b/mediacapture-image/MediaStreamTrack-getConstraints-fast.html @@ -21,6 +21,9 @@ sharpness : 6, focusDistance : 7, + pan : 8, + tilt : 9, + zoom : 3.141592 // TODO: torch https://crbug.com/700607. }; diff --git a/mediacapture-image/MediaStreamTrack-getSettings.html b/mediacapture-image/MediaStreamTrack-getSettings.html index 8535f298bfab2e..a1a864c2445d84 100644 --- a/mediacapture-image/MediaStreamTrack-getSettings.html +++ b/mediacapture-image/MediaStreamTrack-getSettings.html @@ -61,6 +61,9 @@ assert_equals(settings.sharpness, mockSettings.sharpness.current); assert_equals(settings.focusDistance, mockSettings.focusDistance.current); + + assert_equals(settings.pan, mockSettings.pan.current); + assert_equals(settings.tilt, mockSettings.tilt.current); assert_equals(settings.zoom, mockSettings.zoom.current); assert_equals(settings.torch, mockSettings.torch, 'torch'); diff --git a/resources/chromium/image_capture.mojom.js b/resources/chromium/image_capture.mojom.js index bf8dd16ca75d4b..3e3456f68b9347 100644 --- a/resources/chromium/image_capture.mojom.js +++ b/resources/chromium/image_capture.mojom.js @@ -24,6 +24,8 @@ MeteringMode.MANUAL = MeteringMode.NONE + 1; MeteringMode.SINGLE_SHOT = MeteringMode.MANUAL + 1; MeteringMode.CONTINUOUS = MeteringMode.SINGLE_SHOT + 1; + MeteringMode.MIN_VALUE = 0, + MeteringMode.MAX_VALUE = 3, MeteringMode.isKnownEnumValue = function(value) { switch (value) { @@ -47,6 +49,8 @@ RedEyeReduction.NEVER = 0; RedEyeReduction.ALWAYS = RedEyeReduction.NEVER + 1; RedEyeReduction.CONTROLLABLE = RedEyeReduction.ALWAYS + 1; + RedEyeReduction.MIN_VALUE = 0, + RedEyeReduction.MAX_VALUE = 2, RedEyeReduction.isKnownEnumValue = function(value) { switch (value) { @@ -69,6 +73,8 @@ FillLightMode.OFF = 0; FillLightMode.AUTO = FillLightMode.OFF + 1; FillLightMode.FLASH = FillLightMode.AUTO + 1; + FillLightMode.MIN_VALUE = 0, + FillLightMode.MAX_VALUE = 2, FillLightMode.isKnownEnumValue = function(value) { switch (value) { @@ -175,6 +181,8 @@ this.saturation = null; this.sharpness = null; this.focusDistance = null; + this.pan = null; + this.tilt = null; this.zoom = null; this.redEyeReduction = 0; this.height = null; @@ -195,7 +203,7 @@ return err; var kVersionSizes = [ - {version: 0, numBytes: 168} + {version: 0, numBytes: 184} ]; err = messageValidator.validateStructVersion(offset, kVersionSizes); if (err !== validator.validationError.NONE) @@ -298,41 +306,53 @@ return err; - // validate PhotoState.zoom + // validate PhotoState.pan err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 120, Range, false); if (err !== validator.validationError.NONE) return err; + // validate PhotoState.tilt + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 128, Range, false); + if (err !== validator.validationError.NONE) + return err; + + + // validate PhotoState.zoom + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 136, Range, false); + if (err !== validator.validationError.NONE) + return err; + + // validate PhotoState.redEyeReduction - err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 128, RedEyeReduction); + err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 144, RedEyeReduction); if (err !== validator.validationError.NONE) return err; // validate PhotoState.height - err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 136, Range, false); + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 152, Range, false); if (err !== validator.validationError.NONE) return err; // validate PhotoState.width - err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 144, Range, false); + err = messageValidator.validateStructPointer(offset + codec.kStructHeaderSize + 160, Range, false); if (err !== validator.validationError.NONE) return err; // validate PhotoState.fillLightMode - err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 152, 4, new codec.Enum(FillLightMode), false, [0], 0); + err = messageValidator.validateArrayPointer(offset + codec.kStructHeaderSize + 168, 4, new codec.Enum(FillLightMode), false, [0], 0); if (err !== validator.validationError.NONE) return err; return validator.validationError.NONE; }; - PhotoState.encodedSize = codec.kStructHeaderSize + 160; + PhotoState.encodedSize = codec.kStructHeaderSize + 176; PhotoState.decode = function(decoder) { var packed; @@ -361,6 +381,8 @@ val.saturation = decoder.decodeStructPointer(Range); val.sharpness = decoder.decodeStructPointer(Range); val.focusDistance = decoder.decodeStructPointer(Range); + val.pan = decoder.decodeStructPointer(Range); + val.tilt = decoder.decodeStructPointer(Range); val.zoom = decoder.decodeStructPointer(Range); val.redEyeReduction = decoder.decodeStruct(codec.Int32); decoder.skip(1); @@ -400,6 +422,8 @@ encoder.encodeStructPointer(Range, val.saturation); encoder.encodeStructPointer(Range, val.sharpness); encoder.encodeStructPointer(Range, val.focusDistance); + encoder.encodeStructPointer(Range, val.pan); + encoder.encodeStructPointer(Range, val.tilt); encoder.encodeStructPointer(Range, val.zoom); encoder.encodeStruct(codec.Int32, val.redEyeReduction); encoder.skip(1); @@ -483,6 +507,8 @@ this.hasSaturation = false; this.hasSharpness = false; this.hasFocusDistance = false; + this.hasPan = false; + this.hasTilt = false; this.hasZoom = false; this.hasTorch = false; this.torch = false; @@ -504,6 +530,8 @@ this.saturation = 0; this.sharpness = 0; this.focusDistance = 0; + this.pan = 0; + this.tilt = 0; this.zoom = 0; this.fillLightMode = 0; this.width = 0; @@ -523,7 +551,7 @@ return err; var kVersionSizes = [ - {version: 0, numBytes: 136} + {version: 0, numBytes: 152} ]; err = messageValidator.validateStructVersion(offset, kVersionSizes); if (err !== validator.validationError.NONE) @@ -575,13 +603,17 @@ + + + + // validate PhotoSettings.fillLightMode - err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 104, FillLightMode); + err = messageValidator.validateEnum(offset + codec.kStructHeaderSize + 120, FillLightMode); if (err !== validator.validationError.NONE) return err; @@ -594,7 +626,7 @@ return validator.validationError.NONE; }; - PhotoSettings.encodedSize = codec.kStructHeaderSize + 128; + PhotoSettings.encodedSize = codec.kStructHeaderSize + 144; PhotoSettings.decode = function(decoder) { var packed; @@ -615,15 +647,17 @@ val.hasSaturation = (packed >> 1) & 1 ? true : false; val.hasSharpness = (packed >> 2) & 1 ? true : false; val.hasFocusDistance = (packed >> 3) & 1 ? true : false; - val.hasZoom = (packed >> 4) & 1 ? true : false; - val.hasTorch = (packed >> 5) & 1 ? true : false; - val.torch = (packed >> 6) & 1 ? true : false; - val.hasFillLightMode = (packed >> 7) & 1 ? true : false; + val.hasPan = (packed >> 4) & 1 ? true : false; + val.hasTilt = (packed >> 5) & 1 ? true : false; + val.hasZoom = (packed >> 6) & 1 ? true : false; + val.hasTorch = (packed >> 7) & 1 ? true : false; packed = decoder.readUint8(); - val.hasWidth = (packed >> 0) & 1 ? true : false; - val.hasHeight = (packed >> 1) & 1 ? true : false; - val.hasRedEyeReduction = (packed >> 2) & 1 ? true : false; - val.redEyeReduction = (packed >> 3) & 1 ? true : false; + val.torch = (packed >> 0) & 1 ? true : false; + val.hasFillLightMode = (packed >> 1) & 1 ? true : false; + val.hasWidth = (packed >> 2) & 1 ? true : false; + val.hasHeight = (packed >> 3) & 1 ? true : false; + val.hasRedEyeReduction = (packed >> 4) & 1 ? true : false; + val.redEyeReduction = (packed >> 5) & 1 ? true : false; decoder.skip(1); val.whiteBalanceMode = decoder.decodeStruct(codec.Int32); val.exposureMode = decoder.decodeStruct(codec.Int32); @@ -638,6 +672,8 @@ val.saturation = decoder.decodeStruct(codec.Double); val.sharpness = decoder.decodeStruct(codec.Double); val.focusDistance = decoder.decodeStruct(codec.Double); + val.pan = decoder.decodeStruct(codec.Double); + val.tilt = decoder.decodeStruct(codec.Double); val.zoom = decoder.decodeStruct(codec.Double); val.fillLightMode = decoder.decodeStruct(codec.Int32); decoder.skip(1); @@ -668,16 +704,18 @@ packed |= (val.hasSaturation & 1) << 1 packed |= (val.hasSharpness & 1) << 2 packed |= (val.hasFocusDistance & 1) << 3 - packed |= (val.hasZoom & 1) << 4 - packed |= (val.hasTorch & 1) << 5 - packed |= (val.torch & 1) << 6 - packed |= (val.hasFillLightMode & 1) << 7 + packed |= (val.hasPan & 1) << 4 + packed |= (val.hasTilt & 1) << 5 + packed |= (val.hasZoom & 1) << 6 + packed |= (val.hasTorch & 1) << 7 encoder.writeUint8(packed); packed = 0; - packed |= (val.hasWidth & 1) << 0 - packed |= (val.hasHeight & 1) << 1 - packed |= (val.hasRedEyeReduction & 1) << 2 - packed |= (val.redEyeReduction & 1) << 3 + packed |= (val.torch & 1) << 0 + packed |= (val.hasFillLightMode & 1) << 1 + packed |= (val.hasWidth & 1) << 2 + packed |= (val.hasHeight & 1) << 3 + packed |= (val.hasRedEyeReduction & 1) << 4 + packed |= (val.redEyeReduction & 1) << 5 encoder.writeUint8(packed); encoder.skip(1); encoder.encodeStruct(codec.Int32, val.whiteBalanceMode); @@ -693,6 +731,8 @@ encoder.encodeStruct(codec.Double, val.saturation); encoder.encodeStruct(codec.Double, val.sharpness); encoder.encodeStruct(codec.Double, val.focusDistance); + encoder.encodeStruct(codec.Double, val.pan); + encoder.encodeStruct(codec.Double, val.tilt); encoder.encodeStruct(codec.Double, val.zoom); encoder.encodeStruct(codec.Int32, val.fillLightMode); encoder.skip(1); diff --git a/resources/chromium/mock-imagecapture.js b/resources/chromium/mock-imagecapture.js index 329cbc3a761dc5..eec414bd032a2d 100644 --- a/resources/chromium/mock-imagecapture.js +++ b/resources/chromium/mock-imagecapture.js @@ -91,6 +91,20 @@ var ImageCaptureTest = (() => { step: 1.0 }, + pan: { + min: 0.0, + max: 10.0, + current: 5.0, + step: 2.0 + }, + + tilt: { + min: 0.0, + max: 10.0, + current: 5.0, + step: 2.0 + }, + zoom: { min: 0.0, max: 10.0, @@ -140,6 +154,10 @@ var ImageCaptureTest = (() => { this.state_.state.height.current = settings.height; if (settings.hasWidth) this.state_.state.width.current = settings.width; + if (settings.hasPan) + this.state_.state.pan.current = settings.pan; + if (settings.hasTilt) + this.state_.state.tilt.current = settings.tilt; if (settings.hasZoom) this.state_.state.zoom.current = settings.zoom; if (settings.hasFocusMode)