camera: Support manual focus distance control on USB camera
This CL adds manual focus distance control in USB HAL.
BUG=b:151048287, 1068176
TEST=Test focus distance on logitech camera on eve.
Pass CtsCameraTestCases on morphius
Change-Id: Icbe92186daaaee1e8b87a6346f15055d5644f1f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2359065
Tested-by: Hsu Wei-Cheng <mojahsu@chromium.org>
Commit-Queue: Hsu Wei-Cheng <mojahsu@chromium.org>
Reviewed-by: Ren-Pei Zeng <kamesan@chromium.org>
diff --git a/camera/hal/usb/metadata_handler.cc b/camera/hal/usb/metadata_handler.cc
index 7279099..0d10d0d 100644
--- a/camera/hal/usb/metadata_handler.cc
+++ b/camera/hal/usb/metadata_handler.cc
@@ -91,6 +91,24 @@
vert_crop_factor};
}
+// The unit of ANDROID_LENS_FOCUS_DISTANCE is diopters (1/meter), but the unit
+// of V4L2_CID_FOCUS_ABSOLUTE is undefined. We map the V4L2 value to diopters by
+// (value - minimum) / normalize_factor where |value| is in [minimum, maximum].
+// We calculate a proper |normalize_factor| by assuming the minimum focus
+// distance of USB cameras is >= 1cm (tested on real webcams), i.e. max diopter
+// is <= 100, and only use power of 10s for readability.
+// For example, V4L2 range [0, 250] will map to Android range [0, 25], where the
+// minimum focus distance is 1m/25=4cm. This function takes |v4l2_range|
+// (== maximum - minimum) and returns |normalize_factor|.
+uint32_t GetNormalizeFactorForV4l2FocusRange(float v4l2_range) {
+ uint32_t normalize_factor = 1;
+
+ while (v4l2_range / normalize_factor > 100.0)
+ normalize_factor *= 10;
+
+ return normalize_factor;
+}
+
class MetadataUpdater {
public:
explicit MetadataUpdater(android::CameraMetadata* metadata)
@@ -178,7 +196,10 @@
const DeviceInfo& device_info,
V4L2CameraDevice* device,
const SupportedFormats& supported_formats)
- : device_info_(device_info), device_(device), af_trigger_(false) {
+ : device_info_(device_info),
+ device_(device),
+ af_trigger_(false),
+ focus_distance_normalize_factor_(0) {
// MetadataBase::operator= will make a copy of camera_metadata_t.
static_metadata_ = &static_metadata;
request_template_ = &request_template;
@@ -220,6 +241,14 @@
is_zoom_control_supported_ = V4L2CameraDevice::IsControlSupported(
device_info.device_path, kControlZoom);
+ if (V4L2CameraDevice::IsFocusDistanceSupported(device_info.device_path,
+ &focus_distance_range_)) {
+ float full_range =
+ focus_distance_range_.maximum - focus_distance_range_.minimum;
+ focus_distance_normalize_factor_ =
+ GetNormalizeFactorForV4l2FocusRange(full_range);
+ }
+
thread_checker_.DetachFromThread();
}
@@ -769,6 +798,7 @@
ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
ANDROID_LENS_INFO_HYPERFOCAL_DISTANCE,
+ ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
ANDROID_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES,
ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
ANDROID_REQUEST_MAX_NUM_INPUT_STREAMS,
@@ -806,7 +836,6 @@
available_characteristics_keys.end(),
{
ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS,
- ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
});
}
@@ -831,12 +860,6 @@
update_request(ANDROID_LENS_FOCAL_LENGTH,
device_info.lens_info_available_focal_lengths[0]);
- update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
- device_info.lens_info_minimum_focus_distance);
-
- update_request(ANDROID_LENS_FOCUS_DISTANCE,
- device_info.lens_info_optimal_focus_distance);
-
update_static(
ANDROID_SENSOR_INFO_PHYSICAL_SIZE,
std::vector<float>{device_info.sensor_info_physical_size_width,
@@ -867,21 +890,6 @@
update_request(ANDROID_LENS_FOCAL_LENGTH, kDefaultAvailableFocalLength);
}
- if (device_info.lens_info_minimum_focus_distance > 0) {
- update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
- device_info.lens_info_minimum_focus_distance);
- } else {
- update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
- kDefaultMinimumFocusDistance);
- }
-
- if (device_info.lens_info_optimal_focus_distance > 0) {
- update_request(ANDROID_LENS_FOCUS_DISTANCE,
- device_info.lens_info_optimal_focus_distance);
- } else {
- update_request(ANDROID_LENS_FOCUS_DISTANCE, kDefaultLensFocusDistance);
- }
-
if (device_info.sensor_info_pixel_array_size_width > 0 &&
device_info.sensor_info_pixel_array_size_height > 0) {
update_static(ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
@@ -896,31 +904,11 @@
ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL);
}
- update_static(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
- ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
-
update_static(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES,
ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO);
update_request(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO);
- bool support_af =
- V4L2CameraDevice::IsAutoFocusSupported(device_info.device_path);
- if (support_af) {
- update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
- std::vector<uint8_t>{ANDROID_CONTROL_AF_MODE_OFF,
- ANDROID_CONTROL_AF_MODE_AUTO});
- update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_AUTO);
- } else {
- update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
- ANDROID_CONTROL_AF_MODE_OFF);
- update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_OFF);
- // If auto focus is not supported, the minimum focus distance should be 0.
- // Overwrite the value here since there are many camera modules have wrong
- // config.
- update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0.0f);
- }
-
// Set vendor tags for specified boards.
if (device_info.quirks & kQuirkMonocle) {
int32_t timestamp_sync =
@@ -1008,6 +996,38 @@
available_request_keys.push_back(ANDROID_SENSOR_EXPOSURE_TIME);
}
+ // The unit of V4L2 focus distance is undefined, so set it to uncalibrated.
+ update_static(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION,
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
+ if (V4L2CameraDevice::IsControlSupported(device_info.device_path,
+ kControlFocusAuto)) {
+ update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ std::vector<uint8_t>{ANDROID_CONTROL_AF_MODE_OFF,
+ ANDROID_CONTROL_AF_MODE_AUTO});
+ update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_AUTO);
+ update_request(ANDROID_LENS_FOCUS_DISTANCE, 0.0f);
+ if (V4L2CameraDevice::IsFocusDistanceSupported(device_info.device_path,
+ &range)) {
+ float full_range = range.maximum - range.minimum;
+ uint32_t factor = GetNormalizeFactorForV4l2FocusRange(full_range);
+ update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+ full_range / factor);
+ } else {
+ if (device_info.lens_info_minimum_focus_distance > 0) {
+ update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+ 1.0f / device_info.lens_info_minimum_focus_distance);
+ } else {
+ update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE,
+ 1.0f / kDefaultMinimumFocusDistance);
+ }
+ }
+ } else {
+ update_static(ANDROID_CONTROL_AF_AVAILABLE_MODES,
+ ANDROID_CONTROL_AF_MODE_OFF);
+ update_request(ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AF_MODE_OFF);
+ update_static(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 0.0f);
+ }
+
update_static(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
available_characteristics_keys);
update_static(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, available_request_keys);
@@ -1133,9 +1153,34 @@
if (metadata->exists(ANDROID_CONTROL_AF_MODE)) {
camera_metadata_entry entry = metadata->find(ANDROID_CONTROL_AF_MODE);
if (entry.data.u8[0] == ANDROID_CONTROL_AF_MODE_OFF) {
- device_->SetAutoFocus(false);
+ if (metadata->exists(ANDROID_LENS_FOCUS_DISTANCE) &&
+ focus_distance_normalize_factor_ > 0) {
+ entry = metadata->find(ANDROID_LENS_FOCUS_DISTANCE);
+ int32_t distance =
+ static_cast<int32_t>(entry.data.f[0] *
+ focus_distance_normalize_factor_) +
+ focus_distance_range_.minimum;
+ distance = std::min(distance, focus_distance_range_.maximum);
+ device_->SetAutoFocus(false);
+ device_->SetFocusDistance(distance);
+ int32_t focus_distance;
+ device_->GetControlValue(kControlFocusDistance, &focus_distance);
+ float simulate_diopters =
+ static_cast<float>(focus_distance - focus_distance_range_.minimum) /
+ focus_distance_normalize_factor_;
+ update_request(ANDROID_LENS_FOCUS_DISTANCE, simulate_diopters);
+ } else {
+ device_->SetAutoFocus(false);
+ }
} else if (entry.data.u8[0] == ANDROID_CONTROL_AF_MODE_AUTO) {
device_->SetAutoFocus(true);
+ float diopters;
+ if (device_info_.lens_info_optimal_focus_distance > 0) {
+ diopters = 1.0 / device_info_.lens_info_optimal_focus_distance;
+ } else {
+ diopters = 1.0 / kDefaultLensFocusDistance;
+ }
+ update_request(ANDROID_LENS_FOCUS_DISTANCE, diopters);
}
}
diff --git a/camera/hal/usb/metadata_handler.h b/camera/hal/usb/metadata_handler.h
index de11d9c..e9e3b4c 100644
--- a/camera/hal/usb/metadata_handler.h
+++ b/camera/hal/usb/metadata_handler.h
@@ -139,6 +139,9 @@
bool is_sharpness_control_supported_;
bool is_tilt_control_supported_;
bool is_zoom_control_supported_;
+
+ uint32_t focus_distance_normalize_factor_;
+ ControlRange focus_distance_range_;
};
} // namespace cros
diff --git a/camera/hal/usb/v4l2_camera_device.cc b/camera/hal/usb/v4l2_camera_device.cc
index c529128..cb63cf0 100644
--- a/camera/hal/usb/v4l2_camera_device.cc
+++ b/camera/hal/usb/v4l2_camera_device.cc
@@ -64,6 +64,12 @@
case kControlExposureTime:
return V4L2_CID_EXPOSURE_ABSOLUTE;
+ case kControlFocusAuto:
+ return V4L2_CID_FOCUS_AUTO;
+
+ case kControlFocusDistance:
+ return V4L2_CID_FOCUS_ABSOLUTE;
+
case kControlPan:
return V4L2_CID_PAN_ABSOLUTE;
@@ -108,6 +114,12 @@
case kControlExposureTime:
return "exposure time";
+ case kControlFocusAuto:
+ return "auto focus";
+
+ case kControlFocusDistance:
+ return "focus distance";
+
case kControlPan:
return "pan";
@@ -152,6 +164,12 @@
case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
return "V4L2_CID_EXPOSURE_AUTO_PRIORITY";
+ case V4L2_CID_FOCUS_ABSOLUTE:
+ return "V4L2_CID_FOCUS_ABSOLUTE";
+
+ case V4L2_CID_FOCUS_AUTO:
+ return "V4L2_CID_FOCUS_AUTO";
+
case V4L2_CID_PAN_ABSOLUTE:
return "V4L2_CID_PAN_ABSOLUTE";
@@ -244,20 +262,23 @@
}
// Initial autofocus state.
- struct v4l2_control control;
- control.id = V4L2_CID_FOCUS_AUTO;
- ret = TEMP_FAILURE_RETRY(ioctl(device_fd_.get(), VIDIOC_G_CTRL, &control));
- if (ret < 0) {
- LOGF(WARNING) << "Failed to get V4L2_CID_FOCUS_AUTO";
- autofocus_supported_ = false;
- autofocus_on_ = false;
- } else {
- autofocus_supported_ = true;
- autofocus_on_ = control.value;
+ int32_t value;
+ focus_auto_supported_ = IsControlSupported(kControlFocusAuto) &&
+ GetControlValue(kControlFocusAuto, &value) == 0;
+ if (focus_auto_supported_) {
+ LOGF(INFO) << "Device supports auto focus control, current mode is "
+ << (value == 0 ? "Off" : "Auto");
+ }
+ focus_distance_supported_ = IsControlSupported(kControlFocusDistance);
+ if (focus_distance_supported_) {
+ LOGF(INFO) << "Device supports focus distance control";
+ // Focus distance is valid when focus mode is off.
+ if (value == 0 && GetControlValue(kControlFocusDistance, &value) == 0) {
+ LOGF(INFO) << "Current distance is " << value;
+ }
}
// Query the initial auto white balance state.
- int32_t value;
white_balance_control_supported_ =
IsControlSupported(kControlAutoWhiteBalance) &&
IsControlSupported(kControlWhiteBalanceTemperature);
@@ -633,19 +654,30 @@
}
int V4L2CameraDevice::SetAutoFocus(bool enable) {
- if (!autofocus_supported_ || enable == autofocus_on_)
+ if (!focus_auto_supported_) {
+ // Off mode is default supported
+ if (enable) {
+ LOGF(WARNING)
+ << "Setting auto focus while device doesn't support. Ignored";
+ }
return 0;
- int ret;
- struct v4l2_control control;
- control.id = V4L2_CID_FOCUS_AUTO;
- control.value = enable ? 1 : 0;
- ret = TEMP_FAILURE_RETRY(ioctl(device_fd_.get(), VIDIOC_S_CTRL, &control));
- if (ret < 0) {
- LOGF(WARNING) << "Failed to set V4L2_CID_FOCUS_AUTO";
- } else {
- autofocus_on_ = enable;
}
- return ret;
+
+ if (enable && control_values_.count(kControlFocusDistance)) {
+ control_values_.erase(kControlFocusDistance);
+ }
+
+ return SetControlValue(kControlFocusAuto, enable ? 1 : 0);
+}
+
+int V4L2CameraDevice::SetFocusDistance(int32_t distance) {
+ if (!focus_distance_supported_) {
+ LOGF(WARNING) << "Setting focus distance while devcie doesn't support. "
+ << "Ignored.";
+ return 0;
+ }
+
+ return SetControlValue(kControlFocusDistance, distance);
}
int V4L2CameraDevice::SetExposureTimeHundredUs(uint32_t exposure_time) {
@@ -1281,19 +1313,21 @@
}
// static
-bool V4L2CameraDevice::IsAutoFocusSupported(const std::string& device_path) {
- base::ScopedFD fd(RetryDeviceOpen(device_path, O_RDONLY));
- if (!fd.is_valid()) {
- PLOGF(ERROR) << "Failed to open " << device_path;
+bool V4L2CameraDevice::IsFocusDistanceSupported(
+ const std::string& device_path, ControlRange* focus_distance_range) {
+ DCHECK(focus_distance_range != nullptr);
+
+ if (!IsControlSupported(device_path, kControlFocusAuto))
+ return false;
+
+ ControlInfo info;
+ if (QueryControl(device_path, kControlFocusDistance, &info) != 0) {
return false;
}
- struct v4l2_queryctrl query_ctrl;
- query_ctrl.id = V4L2_CID_FOCUS_AUTO;
- if (TEMP_FAILURE_RETRY(ioctl(fd.get(), VIDIOC_QUERYCTRL, &query_ctrl)) < 0) {
- LOGF(WARNING) << "Failed to query V4L2_CID_FOCUS_AUTO";
- return false;
- }
- return !(query_ctrl.flags & V4L2_CTRL_FLAG_DISABLED);
+
+ *focus_distance_range = info.range;
+
+ return true;
}
// static
diff --git a/camera/hal/usb/v4l2_camera_device.h b/camera/hal/usb/v4l2_camera_device.h
index 58a8765..89c6a13 100644
--- a/camera/hal/usb/v4l2_camera_device.h
+++ b/camera/hal/usb/v4l2_camera_device.h
@@ -39,6 +39,8 @@
enum ControlType {
kControlAutoWhiteBalance,
kControlBrightness,
+ kControlFocusAuto,
+ kControlFocusDistance,
kControlContrast,
kControlExposureAuto,
kControlExposureAutoPriority, // 0 for constant frame rate
@@ -117,6 +119,9 @@
// |-errno|.
int SetAutoFocus(bool enable);
+ // Return 0 if focus distance is set successfully. Otherwise, return |-errno|.
+ int SetFocusDistance(int32_t distance);
+
// Return 0 if device sets color tepmerature successfully. Otherwise, return
// |-errno|. Set |color_temperature| to |kColorTemperatureAuto| means auto
// white balance mode.
@@ -160,7 +165,10 @@
static PowerLineFrequency GetPowerLineFrequency(
const std::string& device_path);
- static bool IsAutoFocusSupported(const std::string& device_path);
+ // If the device supports manual focus distance, returns the focus distance
+ // range to |focus_distance_range|.
+ static bool IsFocusDistanceSupported(const std::string& device_path,
+ ControlRange* focus_distance_range);
// If the device supports manual exposure time, returns the exposure time
// range to |exposure_time_range|.
@@ -239,8 +247,8 @@
bool stream_on_;
// AF state
- bool autofocus_on_;
- bool autofocus_supported_;
+ bool focus_auto_supported_;
+ bool focus_distance_supported_;
bool white_balance_control_supported_;