Commit 776b8ced authored by Rijubrata Bhaumik's avatar Rijubrata Bhaumik Committed by Commit Bot

[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/

Putting Pan/Tilt feature behind a flag:
chrome --enable-blink-features=MediaCapturePanTilt

Intent to Implement and Ship discussions:
https://groups.google.com/a/chromium.org/d/msg/blink-dev/j-Q08QgBipM/F3a5sau1BwAJ


Bug: 934063
Change-Id: I552c4c8be717c3b67c4d91f826a1f16850430fa4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1508519Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Commit-Queue: Rijubrata Bhaumik <rijubrata.bhaumik@intel.com>
Cr-Commit-Position: refs/heads/master@{#681332}
parent 74b51bcd
......@@ -32,9 +32,13 @@ namespace content {
#if defined(OS_ANDROID)
// TODO(crbug.com/793859): Re-enable test on Android as soon as the cause for
// the bug is understood and fixed.
#define MAYBE_ManipulatePan DISABLED_ManipulatePan
#define MAYBE_ManipulateTilt DISABLED_ManipulateTilt
#define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
#define MAYBE_ManipulateExposureTime DISABLED_ManipulateExposureTime
#else
#define MAYBE_ManipulatePan ManipulatePan
#define MAYBE_ManipulateTilt ManipulateTilt
#define MAYBE_ManipulateZoom ManipulateZoom
#define MAYBE_ManipulateExposureTime ManipulateExposureTime
#endif
......@@ -92,6 +96,9 @@ class WebRtcImageCaptureBrowserTestBase
ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kUseFakeDeviceForMediaStream));
// Enable Pan/Tilt for testing.
command_line->AppendSwitchASCII("--enable-blink-features",
"MediaCapturePanTilt");
}
void SetUp() override {
......@@ -214,6 +221,18 @@ IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
ASSERT_TRUE(RunImageCaptureTestCase("testCreateAndGetTrackSettings()"));
}
IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
MAYBE_ManipulatePan) {
embedded_test_server()->StartAcceptingConnections();
ASSERT_TRUE(RunImageCaptureTestCase("testManipulatePan()"));
}
IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
MAYBE_ManipulateTilt) {
embedded_test_server()->StartAcceptingConnections();
ASSERT_TRUE(RunImageCaptureTestCase("testManipulateTilt()"));
}
IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
MAYBE_ManipulateZoom) {
embedded_test_server()->StartAcceptingConnections();
......
......@@ -149,6 +149,88 @@ function testCreateAndGetTrackSettings() {
});
}
// Tries to read, set and read back the pan capability if available.
function testManipulatePan() {
var newPan = -1;
var imageCapturer;
var panTolerance;
navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
.then(stream => {
assertEquals('video', stream.getVideoTracks()[0].kind);
imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
// TODO(mcasas): Before accesing synchronous track APIs we need a delay,
// use instead a round trip of capabilities: https://crbug.com/711524.
return imageCapturer.getPhotoCapabilities();
})
.then(capabilities => {
const trackCapabilities = imageCapturer.track.getCapabilities();
if (trackCapabilities.pan === undefined) {
console.log('pan not supported, skipping test');
reportTestSuccess();
return;
}
const currentPan = imageCapturer.track.getSettings().pan;
newPan = currentPan + trackCapabilities.pan.step;
newPan = Math.min(newPan, trackCapabilities.pan.max);
console.log("Setting pan from " + currentPan + " to " + newPan);
panTolerance = trackCapabilities.pan.step / 10;
return imageCapturer.track.applyConstraints({advanced : [{pan : newPan}]});
})
.then(() => {
assertEquals(newPan,
imageCapturer.track.getConstraints().advanced[0].pan);
assertTrue(Math.abs(newPan - imageCapturer.track.getSettings().pan)
< panTolerance);
reportTestSuccess();
})
.catch(err => {
return failTest(err.toString());
});
}
// Tries to read, set and read back the tilt capability if available.
function testManipulateTilt() {
var newTilt = -1;
var imageCapturer;
var tiltTolerance;
navigator.mediaDevices.getUserMedia({"video" : CONSTRAINTS})
.then(stream => {
assertEquals('video', stream.getVideoTracks()[0].kind);
imageCapturer = new ImageCapture(stream.getVideoTracks()[0]);
// TODO(mcasas): Before accesing synchronous track APIs we need a delay,
// use instead a round trip of capabilities: https://crbug.com/711524.
return imageCapturer.getPhotoCapabilities();
})
.then(capabilities => {
const trackCapabilities = imageCapturer.track.getCapabilities();
if (trackCapabilities.tilt === undefined) {
console.log('tilt not supported, skipping test');
reportTestSuccess();
return;
}
const currentTilt = imageCapturer.track.getSettings().tilt;
newTilt = currentTilt + trackCapabilities.tilt.step;
newTilt = Math.min(newTilt, trackCapabilities.tilt.max);
console.log("Setting tilt from " + currentTilt + " to " + newTilt);
tiltTolerance = trackCapabilities.tilt.step / 10;
return imageCapturer.track.applyConstraints({advanced : [{tilt : newTilt}]});
})
.then(() => {
assertEquals(newTilt,
imageCapturer.track.getConstraints().advanced[0].tilt);
assertTrue(Math.abs(newTilt - imageCapturer.track.getSettings().tilt)
< tiltTolerance);
reportTestSuccess();
})
.catch(err => {
return failTest(err.toString());
});
}
// Tries to read, set and read back the zoom capability if available.
function testManipulateZoom() {
var newZoom = -1;
......
......@@ -47,6 +47,9 @@ struct PhotoState {
Range sharpness;
Range focus_distance;
Range pan;
Range tilt;
Range zoom;
bool supports_torch;
......@@ -101,6 +104,11 @@ struct PhotoSettings {
bool has_focus_distance;
double focus_distance;
bool has_pan;
double pan;
bool has_tilt;
double tilt;
bool has_zoom;
double zoom;
......
......@@ -19,6 +19,8 @@ media::mojom::PhotoStatePtr CreateEmptyPhotoState() {
photo_capabilities->contrast = media::mojom::Range::New();
photo_capabilities->saturation = media::mojom::Range::New();
photo_capabilities->sharpness = media::mojom::Range::New();
photo_capabilities->pan = media::mojom::Range::New();
photo_capabilities->tilt = media::mojom::Range::New();
photo_capabilities->zoom = media::mojom::Range::New();
photo_capabilities->focus_distance = media::mojom::Range::New();
photo_capabilities->torch = false;
......
......@@ -39,6 +39,14 @@ static const int kBeepInterval = 500;
// Gradient travels from bottom to top in 5 seconds.
static const float kGradientFrequency = 1.f / 5;
static const double kMinPan = 100.0;
static const double kMaxPan = 400.0;
static const double kPanStep = 1.0;
static const double kMinTilt = 100.0;
static const double kMaxTilt = 400.0;
static const double kTiltStep = 1.0;
static const double kMinZoom = 100.0;
static const double kMaxZoom = 400.0;
static const double kZoomStep = 1.0;
......@@ -541,6 +549,18 @@ void FakePhotoDevice::GetPhotoState(
photo_state->focus_distance->min = kMinFocusDistance;
photo_state->focus_distance->step = kFocusDistanceStep;
photo_state->pan = mojom::Range::New();
photo_state->pan->current = fake_device_state_->pan;
photo_state->pan->max = kMaxPan;
photo_state->pan->min = kMinPan;
photo_state->pan->step = kPanStep;
photo_state->tilt = mojom::Range::New();
photo_state->tilt->current = fake_device_state_->tilt;
photo_state->tilt->max = kMaxTilt;
photo_state->tilt->min = kMinTilt;
photo_state->tilt->step = kTiltStep;
photo_state->zoom = mojom::Range::New();
photo_state->zoom->current = fake_device_state_->zoom;
photo_state->zoom->max = kMaxZoom;
......@@ -579,6 +599,14 @@ void FakePhotoDevice::SetPhotoOptions(
if (config_.should_fail_set_photo_options)
return;
if (settings->has_pan) {
device_state_write_access->pan =
std::max(kMinPan, std::min(settings->pan, kMaxPan));
}
if (settings->has_tilt) {
device_state_write_access->tilt =
std::max(kMinTilt, std::min(settings->tilt, kMaxTilt));
}
if (settings->has_zoom) {
device_state_write_access->zoom =
std::max(kMinZoom, std::min(settings->zoom, kMaxZoom));
......
......@@ -102,12 +102,16 @@ class FakeVideoCaptureDevice : public VideoCaptureDevice {
// This is a separate struct because read-access to it is shared with several
// collaborating classes.
struct FakeDeviceState {
FakeDeviceState(double zoom,
FakeDeviceState(double pan,
double tilt,
double zoom,
double exposure_time,
double focus_distance,
float frame_rate,
VideoPixelFormat pixel_format)
: zoom(zoom),
: pan(pan),
tilt(tilt),
zoom(zoom),
exposure_time(exposure_time),
focus_distance(focus_distance),
format(gfx::Size(), frame_rate, pixel_format) {
......@@ -117,6 +121,8 @@ struct FakeDeviceState {
: mojom::MeteringMode::CONTINUOUS;
}
double pan;
double tilt;
double zoom;
double exposure_time;
mojom::MeteringMode exposure_mode;
......
......@@ -33,6 +33,8 @@ static constexpr std::array<gfx::Size, 5> kDefaultResolutions{
gfx::Size(1280, 720), gfx::Size(1920, 1080)}};
static constexpr std::array<float, 1> kDefaultFrameRates{{20.0f}};
static const double kInitialPan = 100.0;
static const double kInitialTilt = 100.0;
static const double kInitialZoom = 100.0;
static const double kInitialExposureTime = 50.0;
static const double kInitialFocusDistance = 50.0;
......@@ -133,8 +135,9 @@ FakeVideoCaptureDeviceFactory::CreateDeviceWithSettings(
const VideoCaptureFormat& initial_format = settings.supported_formats.front();
auto device_state = std::make_unique<FakeDeviceState>(
kInitialZoom, kInitialExposureTime, kInitialFocusDistance,
initial_format.frame_rate, initial_format.pixel_format);
kInitialPan, kInitialTilt, kInitialZoom, kInitialExposureTime,
kInitialFocusDistance, initial_format.frame_rate,
initial_format.pixel_format);
auto photo_frame_painter = std::make_unique<PacmanFramePainter>(
PacmanFramePainter::Format::SK_N32, device_state.get());
......
......@@ -408,6 +408,17 @@ TEST_F(FakeVideoCaptureDeviceTest, GetAndSetCapabilities) {
EXPECT_EQ(96, state->width->min);
EXPECT_EQ(1920, state->width->max);
EXPECT_EQ(1, state->width->step);
EXPECT_EQ(100, state->pan->min);
EXPECT_EQ(400, state->pan->max);
EXPECT_EQ(1, state->pan->step);
EXPECT_GE(state->pan->current, state->pan->min);
EXPECT_GE(state->pan->max, state->pan->current);
EXPECT_EQ(100, state->tilt->min);
EXPECT_EQ(400, state->tilt->max);
EXPECT_EQ(1, state->tilt->step);
EXPECT_GE(state->tilt->current, state->tilt->min);
EXPECT_GE(state->tilt->max, state->tilt->current);
EXPECT_EQ(100, state->zoom->min);
EXPECT_EQ(400, state->zoom->max);
EXPECT_EQ(1, state->zoom->step);
......
......@@ -370,8 +370,8 @@ void FileVideoCaptureDevice::SetPhotoOptions(mojom::PhotoSettingsPtr settings,
settings->has_color_temperature || settings->has_iso ||
settings->has_brightness || settings->has_contrast ||
settings->has_saturation || settings->has_sharpness ||
settings->has_focus_distance || settings->has_zoom ||
settings->has_fill_light_mode) {
settings->has_focus_distance || settings->has_pan || settings->has_tilt ||
settings->has_zoom || settings->has_fill_light_mode) {
return;
}
......
......@@ -423,6 +423,8 @@ void V4L2CaptureDelegate::GetPhotoState(
mojom::PhotoStatePtr photo_capabilities = mojo::CreateEmptyPhotoState();
photo_capabilities->pan = RetrieveUserControlRange(V4L2_CID_PAN_ABSOLUTE);
photo_capabilities->tilt = RetrieveUserControlRange(V4L2_CID_TILT_ABSOLUTE);
photo_capabilities->zoom = RetrieveUserControlRange(V4L2_CID_ZOOM_ABSOLUTE);
v4l2_queryctrl manual_focus_ctrl = {};
......@@ -529,6 +531,22 @@ void V4L2CaptureDelegate::SetPhotoOptions(
if (!device_fd_.is_valid() || !is_capturing_)
return;
if (settings->has_pan) {
v4l2_control pan_current = {};
pan_current.id = V4L2_CID_PAN_ABSOLUTE;
pan_current.value = settings->pan;
if (DoIoctl(VIDIOC_S_CTRL, &pan_current) < 0)
DPLOG(ERROR) << "setting pan to " << settings->pan;
}
if (settings->has_tilt) {
v4l2_control tilt_current = {};
tilt_current.id = V4L2_CID_TILT_ABSOLUTE;
tilt_current.value = settings->tilt;
if (DoIoctl(VIDIOC_S_CTRL, &tilt_current) < 0)
DPLOG(ERROR) << "setting tilt to " << settings->tilt;
}
if (settings->has_zoom) {
v4l2_control zoom_current = {};
zoom_current.id = V4L2_CID_ZOOM_ABSOLUTE;
......
......@@ -367,6 +367,8 @@ void ImageCapture::SetMediaTrackConstraints(
(constraints->hasSaturation() && !capabilities_->hasSaturation()) ||
(constraints->hasSharpness() && !capabilities_->hasSharpness()) ||
(constraints->hasFocusDistance() && !capabilities_->hasFocusDistance()) ||
(constraints->hasPan() && !capabilities_->hasPan()) ||
(constraints->hasTilt() && !capabilities_->hasTilt()) ||
(constraints->hasZoom() && !capabilities_->hasZoom()) ||
(constraints->hasTorch() && !capabilities_->hasTorch())) {
resolver->Reject(MakeGarbageCollected<DOMException>(
......@@ -566,6 +568,32 @@ void ImageCapture::SetMediaTrackConstraints(
settings->focus_distance = focus_distance;
}
settings->has_pan = constraints->hasPan() && constraints->pan().IsDouble();
if (settings->has_pan) {
const auto pan = constraints->pan().GetAsDouble();
if (pan < capabilities_->pan()->min() ||
pan > capabilities_->pan()->max()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError, "pan setting out of range"));
return;
}
temp_constraints->setPan(constraints->pan());
settings->pan = pan;
}
settings->has_tilt = constraints->hasTilt() && constraints->tilt().IsDouble();
if (settings->has_tilt) {
const auto tilt = constraints->tilt().GetAsDouble();
if (tilt < capabilities_->tilt()->min() ||
tilt > capabilities_->tilt()->max()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotSupportedError, "tilt setting out of range"));
return;
}
temp_constraints->setTilt(constraints->tilt());
settings->tilt = tilt;
}
settings->has_zoom = constraints->hasZoom() && constraints->zoom().IsDouble();
if (settings->has_zoom) {
const auto zoom = constraints->zoom().GetAsDouble();
......@@ -650,6 +678,11 @@ void ImageCapture::GetMediaTrackSettings(MediaTrackSettings* settings) const {
if (settings_->hasFocusDistance())
settings->setFocusDistance(settings_->focusDistance());
if (settings_->hasPan())
settings->setPan(settings_->pan());
if (settings_->hasTilt())
settings->setTilt(settings_->tilt());
if (settings_->hasZoom())
settings->setZoom(settings_->zoom());
if (settings_->hasTorch())
......@@ -877,6 +910,15 @@ void ImageCapture::UpdateMediaTrackCapabilities(
MediaSettingsRange::Create(*photo_state->focus_distance));
settings_->setFocusDistance(photo_state->focus_distance->current);
}
if (photo_state->pan->max != photo_state->pan->min) {
capabilities_->setPan(MediaSettingsRange::Create(*photo_state->pan));
settings_->setPan(photo_state->pan->current);
}
if (photo_state->tilt->max != photo_state->tilt->min) {
capabilities_->setTilt(MediaSettingsRange::Create(*photo_state->tilt));
settings_->setTilt(photo_state->tilt->current);
}
if (photo_state->zoom->max != photo_state->zoom->min) {
capabilities_->setZoom(MediaSettingsRange::Create(*photo_state->zoom));
settings_->setZoom(photo_state->zoom->current);
......
......@@ -77,8 +77,12 @@ bool ConstraintSetHasImageCapture(
constraint_set->hasColorTemperature() || constraint_set->hasIso() ||
constraint_set->hasBrightness() || constraint_set->hasContrast() ||
constraint_set->hasSaturation() || constraint_set->hasSharpness() ||
constraint_set->hasFocusDistance() || constraint_set->hasZoom() ||
constraint_set->hasTorch();
constraint_set->hasFocusDistance() ||
(RuntimeEnabledFeatures::MediaCapturePanTiltEnabled() &&
constraint_set->hasPan()) ||
(RuntimeEnabledFeatures::MediaCapturePanTiltEnabled() &&
constraint_set->hasTilt()) ||
constraint_set->hasZoom() || constraint_set->hasTorch();
}
bool ConstraintSetHasNonImageCapture(
......@@ -451,6 +455,10 @@ MediaTrackConstraints* MediaStreamTrack::getConstraints() const {
image_capture_constraints->hasSaturation() ||
image_capture_constraints->hasSharpness() ||
image_capture_constraints->hasFocusDistance() ||
(RuntimeEnabledFeatures::MediaCapturePanTiltEnabled() &&
image_capture_constraints->hasPan()) ||
(RuntimeEnabledFeatures::MediaCapturePanTiltEnabled() &&
image_capture_constraints->hasTilt()) ||
image_capture_constraints->hasZoom()) {
// Add image capture constraints, if any, as another entry to advanced().
vector.push_back(image_capture_constraints);
......
......@@ -34,6 +34,8 @@ dictionary MediaTrackCapabilities {
MediaSettingsRange saturation;
MediaSettingsRange sharpness;
MediaSettingsRange focusDistance;
MediaSettingsRange pan;
MediaSettingsRange tilt;
MediaSettingsRange zoom;
boolean torch;
};
......@@ -48,6 +48,8 @@ dictionary MediaTrackConstraintSet {
ConstrainDouble saturation;
ConstrainDouble sharpness;
ConstrainDouble focusDistance;
[RuntimeEnabled=MediaCapturePanTilt] ConstrainDouble pan;
[RuntimeEnabled=MediaCapturePanTilt] ConstrainDouble tilt;
ConstrainDouble zoom;
ConstrainBoolean torch;
// The "mandatory" and "_optional" members are retained for conformance
......
......@@ -43,6 +43,8 @@ dictionary MediaTrackSettings {
double saturation;
double sharpness;
double focusDistance;
[RuntimeEnabled=MediaCapturePanTilt] double pan;
[RuntimeEnabled=MediaCapturePanTilt] double tilt;
double zoom;
boolean torch;
......
......@@ -44,9 +44,11 @@ dictionary MediaTrackSupportedConstraints {
boolean iso = true;
boolean brightness = true;
boolean contrast = true;
[RuntimeEnabled=MediaCapturePanTilt] boolean pan = true;
boolean saturation = true;
boolean sharpness = true;
boolean focusDistance = true;
[RuntimeEnabled=MediaCapturePanTilt] boolean tilt = true;
boolean zoom = true;
boolean torch = true;
};
......@@ -887,6 +887,10 @@
name: "MediaCaptureDepthVideoKind",
status: "experimental",
},
{
name: "MediaCapturePanTilt",
status: "experimental",
},
// Set to reflect the MediaCastOverlayButton feature.
{
name: "MediaCastOverlayButton",
......
......@@ -20,6 +20,8 @@ test(function() {
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');
......
......@@ -35,6 +35,8 @@ image_capture_test(async t => {
sharpness : 6,
focusDistance : 7,
pan : 8,
tilt : 9,
zoom : 3.141592,
torch : true
......@@ -89,6 +91,9 @@ image_capture_test(async t => {
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');
......
......@@ -36,6 +36,8 @@ image_capture_test(async (t, imageCaptureTest) => {
sharpness : 6,
focusDistance : 7,
pan : 8,
tilt : 9,
zoom : 3.141592,
torch : true
......@@ -109,6 +111,8 @@ image_capture_test(async (t, imageCaptureTest) => {
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');
......
......@@ -141,6 +141,16 @@ image_capture_test(async (t, imageCaptureTest) => {
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);
......
......@@ -21,6 +21,9 @@ const constraints = { whiteBalanceMode : "manual",
sharpness : 6,
focusDistance : 7,
pan : 8,
tilt : 9,
zoom : 3.141592
// TODO: torch https://crbug.com/700607.
};
......
......@@ -61,6 +61,9 @@ image_capture_test(async (t, imageCaptureTest) => {
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');
......
......@@ -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);
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment