Commit e6c7e8bd authored by Rijubrata Bhaumik's avatar Rijubrata Bhaumik Committed by Commit Bot

[Image Capture] Wire |exposureTime| in Linux/CrOS.

Tested using a brio-stream-4k-hd-webcam,
where we could change the exposure time in manual mode.

command line:
v4l2-ctl -d 1 --set-ctrl exposure_auto=1
v4l2-ctl -d 1 --set-ctrl exposure_absolute=100

GUI:
guvcview  --device=/dev/video1

TEST= run the demo in https://codepen.io/rijuB/pen/gjapBo
use slider to change exposureTime.

BUG=823316

Intent to Implement and Ship discussions:
https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/ls3wQSoHOUY

Change-Id: I2c4faaf8bd475fedefd267aaf83e6c06f55e5afd
Reviewed-on: https://chromium-review.googlesource.com/c/1243288
Commit-Queue: Rijubrata Bhaumik <rijubrata.bhaumik@intel.com>
Reviewed-by: default avatarMiguel Casas <mcasas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603482}
parent de40441f
...@@ -33,6 +33,7 @@ namespace content { ...@@ -33,6 +33,7 @@ namespace content {
#define MAYBE_GetTrackCapabilities GetTrackCapabilities #define MAYBE_GetTrackCapabilities GetTrackCapabilities
#define MAYBE_GetTrackSettings GetTrackSettings #define MAYBE_GetTrackSettings GetTrackSettings
#define MAYBE_ManipulateZoom DISABLED_ManipulateZoom #define MAYBE_ManipulateZoom DISABLED_ManipulateZoom
#define MAYBE_ManipulateExposureTime DISABLED_ManipulateExposureTime
#else #else
#define MAYBE_GetPhotoCapabilities GetPhotoCapabilities #define MAYBE_GetPhotoCapabilities GetPhotoCapabilities
#define MAYBE_GetPhotoSettings GetPhotoSettings #define MAYBE_GetPhotoSettings GetPhotoSettings
...@@ -41,6 +42,7 @@ namespace content { ...@@ -41,6 +42,7 @@ namespace content {
#define MAYBE_GetTrackCapabilities GetTrackCapabilities #define MAYBE_GetTrackCapabilities GetTrackCapabilities
#define MAYBE_GetTrackSettings GetTrackSettings #define MAYBE_GetTrackSettings GetTrackSettings
#define MAYBE_ManipulateZoom ManipulateZoom #define MAYBE_ManipulateZoom ManipulateZoom
#define MAYBE_ManipulateExposureTime ManipulateExposureTime
#endif #endif
namespace { namespace {
...@@ -227,6 +229,12 @@ IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest, ...@@ -227,6 +229,12 @@ IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
ASSERT_TRUE(RunImageCaptureTestCase("testManipulateZoom()")); ASSERT_TRUE(RunImageCaptureTestCase("testManipulateZoom()"));
} }
IN_PROC_BROWSER_TEST_P(WebRtcImageCaptureSucceedsBrowserTest,
MAYBE_ManipulateExposureTime) {
embedded_test_server()->StartAcceptingConnections();
ASSERT_TRUE(RunImageCaptureTestCase("testManipulateExposureTime()"));
}
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
, // Use no prefix, so that these get picked up when using , // Use no prefix, so that these get picked up when using
// --gtest_filter=WebRtc* // --gtest_filter=WebRtc*
......
...@@ -209,6 +209,68 @@ function testManipulateZoom() { ...@@ -209,6 +209,68 @@ function testManipulateZoom() {
}); });
} }
// Tries to read, set and read back the exposureTime capability if available.
function testManipulateExposureTime() {
var newExposureTime = -1;
var imageCapturer;
var exposureTimeTolerance;
navigator.mediaDevices.getUserMedia({"video": CONSTRAINTS})
.then(stream => {
assertEquals('video', stream.getVideoTracks()[0].kind);
return new ImageCapture(stream.getVideoTracks()[0]);
})
.then(capturer => {
imageCapturer = capturer;
// TODO(mcasas): Before accesing synchronous track APIs we need a delay,
// use instead a round trip of capabilities: https://crbug.com/711524.
return capturer.getPhotoCapabilities();
})
.then(capabilities => {
const trackCapabilities = imageCapturer.track.getCapabilities();
if (trackCapabilities.exposureTime === undefined) {
console.log('exposureTime not supported, skipping test');
reportTestSuccess();
return;
}
const currentExposureTime = imageCapturer.track.getSettings().exposureTime;
newExposureTime = currentExposureTime + trackCapabilities.exposureTime
.step;
newExposureTime = Math.min(newExposureTime, trackCapabilities.exposureTime
.max);
console.log("Setting exposureTime from " + currentExposureTime +
" to " +
newExposureTime);
exposureTimeTolerance = trackCapabilities.exposureTime.step /
10;
currentExposureMode = imageCapturer.track.getSettings().exposureMode;
console.log(" exposureMode == " + currentExposureMode);
exposureModeCapabilities = trackCapabilities.exposureMode;
console.log(" exposureModeCapabilities == " +
exposureModeCapabilities);
return imageCapturer.track.applyConstraints({
advanced: [{
exposureMode: "manual",
exposureTime: newExposureTime
}]
});
})
.then(() => {
assertEquals(newExposureTime,
imageCapturer.track.getConstraints().advanced[0].exposureTime
);
assertTrue(Math.abs(newExposureTime - imageCapturer.track.getSettings()
.exposureTime) <
exposureTimeTolerance);
reportTestSuccess();
})
.catch(err => {
return failTest(err.toString());
});
}
</script> </script>
</body> </body>
</html> </html>
...@@ -42,6 +42,10 @@ static const double kMinZoom = 100.0; ...@@ -42,6 +42,10 @@ static const double kMinZoom = 100.0;
static const double kMaxZoom = 400.0; static const double kMaxZoom = 400.0;
static const double kZoomStep = 1.0; static const double kZoomStep = 1.0;
static const double kMinExposureTime = 10.0;
static const double kMaxExposureTime = 100.0;
static const double kExposureTimeStep = 5.0;
// Larger int means better. // Larger int means better.
enum class PixelFormatMatchType : int { enum class PixelFormatMatchType : int {
INCOMPATIBLE = 0, INCOMPATIBLE = 0,
...@@ -457,11 +461,21 @@ void FakePhotoDevice::GetPhotoState( ...@@ -457,11 +461,21 @@ void FakePhotoDevice::GetPhotoState(
mojom::PhotoStatePtr photo_state = mojo::CreateEmptyPhotoState(); mojom::PhotoStatePtr photo_state = mojo::CreateEmptyPhotoState();
photo_state->current_white_balance_mode = mojom::MeteringMode::NONE; photo_state->current_white_balance_mode = mojom::MeteringMode::NONE;
photo_state->current_exposure_mode = mojom::MeteringMode::NONE;
photo_state->supported_exposure_modes.push_back(mojom::MeteringMode::MANUAL);
photo_state->supported_exposure_modes.push_back(
mojom::MeteringMode::CONTINUOUS);
photo_state->current_exposure_mode = fake_device_state_->exposure_mode;
photo_state->current_focus_mode = mojom::MeteringMode::NONE; photo_state->current_focus_mode = mojom::MeteringMode::NONE;
photo_state->exposure_compensation = mojom::Range::New(); photo_state->exposure_compensation = mojom::Range::New();
photo_state->exposure_time = mojom::Range::New(); photo_state->exposure_time = mojom::Range::New();
photo_state->exposure_time->current = fake_device_state_->exposure_time;
photo_state->exposure_time->max = kMaxExposureTime;
photo_state->exposure_time->min = kMinExposureTime;
photo_state->exposure_time->step = kExposureTimeStep;
photo_state->color_temperature = mojom::Range::New(); photo_state->color_temperature = mojom::Range::New();
photo_state->iso = mojom::Range::New(); photo_state->iso = mojom::Range::New();
photo_state->iso->current = 100.0; photo_state->iso->current = 100.0;
...@@ -522,6 +536,12 @@ void FakePhotoDevice::SetPhotoOptions( ...@@ -522,6 +536,12 @@ void FakePhotoDevice::SetPhotoOptions(
device_state_write_access->zoom = device_state_write_access->zoom =
std::max(kMinZoom, std::min(settings->zoom, kMaxZoom)); std::max(kMinZoom, std::min(settings->zoom, kMaxZoom));
} }
if (settings->has_exposure_time) {
device_state_write_access->exposure_time = std::max(
kMinExposureTime, std::min(settings->exposure_time, kMaxExposureTime));
}
if (settings->has_exposure_mode)
device_state_write_access->exposure_mode = mojom::MeteringMode::MANUAL;
std::move(callback).Run(true); std::move(callback).Run(true);
} }
......
...@@ -102,10 +102,19 @@ class FakeVideoCaptureDevice : public VideoCaptureDevice { ...@@ -102,10 +102,19 @@ class FakeVideoCaptureDevice : public VideoCaptureDevice {
// This is a separate struct because read-access to it is shared with several // This is a separate struct because read-access to it is shared with several
// collaborating classes. // collaborating classes.
struct FakeDeviceState { struct FakeDeviceState {
FakeDeviceState(float zoom, float frame_rate, VideoPixelFormat pixel_format) FakeDeviceState(float zoom,
: zoom(zoom), format(gfx::Size(), frame_rate, pixel_format) {} float exposure_time,
mojom::MeteringMode exposure_mode,
float frame_rate,
VideoPixelFormat pixel_format)
: zoom(zoom),
exposure_time(exposure_time),
exposure_mode(exposure_mode),
format(gfx::Size(), frame_rate, pixel_format) {}
uint32_t zoom; uint32_t zoom;
uint32_t exposure_time;
mojom::MeteringMode exposure_mode;
VideoCaptureFormat format; VideoCaptureFormat format;
}; };
......
...@@ -36,6 +36,9 @@ static constexpr std::array<gfx::Size, 5> kDefaultResolutions{ ...@@ -36,6 +36,9 @@ static constexpr std::array<gfx::Size, 5> kDefaultResolutions{
static constexpr std::array<float, 1> kDefaultFrameRates{{20.0f}}; static constexpr std::array<float, 1> kDefaultFrameRates{{20.0f}};
static const double kInitialZoom = 100.0; static const double kInitialZoom = 100.0;
static const double kInitialExposureTime = 50.0;
static const media::mojom::MeteringMode kExposureMode =
media::mojom::MeteringMode::MANUAL;
static const media::VideoPixelFormat kSupportedPixelFormats[] = { static const media::VideoPixelFormat kSupportedPixelFormats[] = {
media::PIXEL_FORMAT_I420, media::PIXEL_FORMAT_Y16, media::PIXEL_FORMAT_I420, media::PIXEL_FORMAT_Y16,
...@@ -133,7 +136,8 @@ FakeVideoCaptureDeviceFactory::CreateDeviceWithSettings( ...@@ -133,7 +136,8 @@ FakeVideoCaptureDeviceFactory::CreateDeviceWithSettings(
const VideoCaptureFormat& initial_format = settings.supported_formats.front(); const VideoCaptureFormat& initial_format = settings.supported_formats.front();
auto device_state = std::make_unique<FakeDeviceState>( auto device_state = std::make_unique<FakeDeviceState>(
kInitialZoom, initial_format.frame_rate, initial_format.pixel_format); kInitialZoom, kInitialExposureTime, kExposureMode,
initial_format.frame_rate, initial_format.pixel_format);
auto photo_frame_painter = std::make_unique<PacmanFramePainter>( auto photo_frame_painter = std::make_unique<PacmanFramePainter>(
PacmanFramePainter::Format::SK_N32, device_state.get()); PacmanFramePainter::Format::SK_N32, device_state.get());
......
...@@ -363,17 +363,17 @@ TEST_F(FakeVideoCaptureDeviceTest, GetAndSetCapabilities) { ...@@ -363,17 +363,17 @@ TEST_F(FakeVideoCaptureDeviceTest, GetAndSetCapabilities) {
const mojom::PhotoState* state = image_capture_client_->state(); const mojom::PhotoState* state = image_capture_client_->state();
ASSERT_TRUE(state); ASSERT_TRUE(state);
EXPECT_EQ(mojom::MeteringMode::NONE, state->current_white_balance_mode); EXPECT_EQ(mojom::MeteringMode::NONE, state->current_white_balance_mode);
EXPECT_EQ(mojom::MeteringMode::NONE, state->current_exposure_mode); EXPECT_EQ(mojom::MeteringMode::MANUAL, state->current_exposure_mode);
EXPECT_EQ(mojom::MeteringMode::NONE, state->current_focus_mode); EXPECT_EQ(mojom::MeteringMode::NONE, state->current_focus_mode);
EXPECT_EQ(0, state->exposure_compensation->min); EXPECT_EQ(0, state->exposure_compensation->min);
EXPECT_EQ(0, state->exposure_compensation->max); EXPECT_EQ(0, state->exposure_compensation->max);
EXPECT_EQ(0, state->exposure_compensation->current); EXPECT_EQ(0, state->exposure_compensation->current);
EXPECT_EQ(0, state->exposure_compensation->step); EXPECT_EQ(0, state->exposure_compensation->step);
EXPECT_EQ(0, state->exposure_time->min); EXPECT_EQ(10, state->exposure_time->min);
EXPECT_EQ(0, state->exposure_time->max); EXPECT_EQ(100, state->exposure_time->max);
EXPECT_EQ(0, state->exposure_time->current); EXPECT_EQ(50, state->exposure_time->current);
EXPECT_EQ(0, state->exposure_time->step); EXPECT_EQ(5, state->exposure_time->step);
EXPECT_EQ(0, state->color_temperature->min); EXPECT_EQ(0, state->color_temperature->min);
EXPECT_EQ(0, state->color_temperature->max); EXPECT_EQ(0, state->color_temperature->max);
EXPECT_EQ(0, state->color_temperature->current); EXPECT_EQ(0, state->color_temperature->current);
......
...@@ -468,7 +468,15 @@ void V4L2CaptureDelegate::GetPhotoState( ...@@ -468,7 +468,15 @@ void V4L2CaptureDelegate::GetPhotoState(
: MeteringMode::CONTINUOUS; : MeteringMode::CONTINUOUS;
} }
// Exposure compensation is valid if V4L2_CID_EXPOSURE_AUTO control is set to
// AUTO, SHUTTER_PRIORITY or APERTURE_PRIORITY. Drivers should interpret the
// values as 0.001 EV units, where the value 1000 stands for +1 EV.
photo_capabilities->exposure_compensation = photo_capabilities->exposure_compensation =
RetrieveUserControlRange(V4L2_CID_AUTO_EXPOSURE_BIAS);
// Determines the exposure time of the camera sensor. Drivers interpret values
// as 100 µs units, same as specs.
photo_capabilities->exposure_time =
RetrieveUserControlRange(V4L2_CID_EXPOSURE_ABSOLUTE); RetrieveUserControlRange(V4L2_CID_EXPOSURE_ABSOLUTE);
photo_capabilities->color_temperature = photo_capabilities->color_temperature =
...@@ -577,15 +585,32 @@ void V4L2CaptureDelegate::SetPhotoOptions( ...@@ -577,15 +585,32 @@ void V4L2CaptureDelegate::SetPhotoOptions(
v4l2_control auto_exposure_current = {}; v4l2_control auto_exposure_current = {};
auto_exposure_current.id = V4L2_CID_EXPOSURE_AUTO; auto_exposure_current.id = V4L2_CID_EXPOSURE_AUTO;
const int result = DoIoctl(VIDIOC_G_CTRL, &auto_exposure_current); const int result = DoIoctl(VIDIOC_G_CTRL, &auto_exposure_current);
// Exposure Compensation can only be applied if Auto Exposure is off. // Exposure Compensation is effective only when V4L2_CID_EXPOSURE_AUTO
if (result >= 0 && auto_exposure_current.value == V4L2_EXPOSURE_MANUAL) { // control is set to AUTO, SHUTTER_PRIORITY or APERTURE_PRIORITY.
if (result >= 0 && auto_exposure_current.value != V4L2_EXPOSURE_MANUAL) {
v4l2_control set_exposure = {}; v4l2_control set_exposure = {};
set_exposure.id = V4L2_CID_EXPOSURE_ABSOLUTE; set_exposure.id = V4L2_CID_AUTO_EXPOSURE_BIAS;
set_exposure.value = settings->exposure_compensation; set_exposure.value = settings->exposure_compensation;
DoIoctl(VIDIOC_S_CTRL, &set_exposure); DoIoctl(VIDIOC_S_CTRL, &set_exposure);
} }
} }
if (settings->has_exposure_time) {
v4l2_control exposure_time_current = {};
exposure_time_current.id = V4L2_CID_EXPOSURE_AUTO;
const int result = DoIoctl(VIDIOC_G_CTRL, &exposure_time_current);
// Exposure time can only be applied if V4L2_CID_EXPOSURE_AUTO is set to
// V4L2_EXPOSURE_MANUAL or V4L2_EXPOSURE_SHUTTER_PRIORITY.
if (result >= 0 &&
(exposure_time_current.value == V4L2_EXPOSURE_MANUAL ||
exposure_time_current.value == V4L2_EXPOSURE_SHUTTER_PRIORITY)) {
v4l2_control set_exposure_time = {};
set_exposure_time.id = V4L2_CID_EXPOSURE_ABSOLUTE;
set_exposure_time.value = settings->exposure_time;
DoIoctl(VIDIOC_S_CTRL, &set_exposure_time);
}
}
if (settings->has_brightness) { if (settings->has_brightness) {
v4l2_control current = {}; v4l2_control current = {};
current.id = V4L2_CID_BRIGHTNESS; current.id = V4L2_CID_BRIGHTNESS;
......
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