[v4] libcamera: pipeline: uvcvideo: Map focus controls
diff mbox series

Message ID 01020192340ab214-3d46266e-b780-407e-941f-8f8d5a841007-000000@eu-west-1.amazonses.com
State New
Headers show
Series
  • [v4] libcamera: pipeline: uvcvideo: Map focus controls
Related show

Commit Message

Cláudio Paulo Sept. 27, 2024, 3:12 p.m. UTC
Added mapping of controls::LensPosition and controls::AfMode to
v4l2 controls V4L2_CID_FOCUS_ABSOLUTE and V4L2_CID_FOCUS_AUTO
respectively when the device supports them.

If not supported, they are not registered.

Signed-off-by: Cláudio Paulo <claudio.paulo@makewise.pt>
---
Fixed issues raised in review.

 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 107 +++++++++++++++++--
 1 file changed, 100 insertions(+), 7 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 6b32fa18..3a0d6ab6 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -6,9 +6,9 @@ 
  */
 
 #include <algorithm>
+#include <cmath>
 #include <fstream>
 #include <map>
-#include <math.h>
 #include <memory>
 #include <set>
 #include <string>
@@ -304,13 +304,33 @@  int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
 		cid = V4L2_CID_EXPOSURE_ABSOLUTE;
 	else if (id == controls::AnalogueGain)
 		cid = V4L2_CID_GAIN;
+	else if (id == controls::LensPosition)
+		cid = V4L2_CID_FOCUS_ABSOLUTE;
+	else if (id == controls::AfMode)
+		cid = V4L2_CID_FOCUS_AUTO;
 	else
 		return -EINVAL;
 
+	/* Check if the device supports this control. */
+	if (controls->idMap()->find(cid) == controls->idMap()->end())
+		return -EINVAL;
+
 	const ControlInfo &v4l2Info = controls->infoMap()->at(cid);
-	int32_t min = v4l2Info.min().get<int32_t>();
-	int32_t def = v4l2Info.def().get<int32_t>();
-	int32_t max = v4l2Info.max().get<int32_t>();
+
+	int32_t min, def, max;
+	if (v4l2Info.min().type() == ControlTypeInteger32) {
+		min = v4l2Info.min().get<int32_t>();
+		def = v4l2Info.def().get<int32_t>();
+		max = v4l2Info.max().get<int32_t>();
+	} else if (v4l2Info.min().type() == ControlTypeBool) {
+		min = v4l2Info.min().get<bool>();
+		def = v4l2Info.def().get<bool>();
+		max = v4l2Info.max().get<bool>();
+	} else {
+		LOG(UVC, Error) << "v4l2 ControlInfo with value type "
+				<< v4l2Info.min().type() << " not mapped";
+		return -EINVAL;
+	}
 
 	/*
 	 * See UVCCameraData::addControl() for explanations of the different
@@ -358,6 +378,21 @@  int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
 		break;
 	}
 
+	case V4L2_CID_FOCUS_ABSOLUTE: {
+		float focusedAt50Cm = 0.15f * (max - min);
+		float scale = focusedAt50Cm / 2.0f;
+
+		float fvalue = value.get<float>() * scale + min;
+		controls->set(cid, static_cast<int32_t>(std::lround(fvalue)));
+		break;
+	}
+
+	case V4L2_CID_FOCUS_AUTO: {
+		int32_t ivalue = value.get<int32_t>() != controls::AfModeManual;
+		controls->set(cid, ivalue);
+		break;
+	}
+
 	default: {
 		int32_t ivalue = value.get<int32_t>();
 		controls->set(cid, ivalue);
@@ -655,14 +690,31 @@  void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 	case V4L2_CID_GAIN:
 		id = &controls::AnalogueGain;
 		break;
+	case V4L2_CID_FOCUS_ABSOLUTE:
+		id = &controls::LensPosition;
+		break;
+	case V4L2_CID_FOCUS_AUTO:
+		id = &controls::AfMode;
+		break;
 	default:
 		return;
 	}
 
 	/* Map the control info. */
-	int32_t min = v4l2Info.min().get<int32_t>();
-	int32_t max = v4l2Info.max().get<int32_t>();
-	int32_t def = v4l2Info.def().get<int32_t>();
+	int32_t min, def, max;
+	if (v4l2Info.min().type() == ControlTypeInteger32) {
+		min = v4l2Info.min().get<int32_t>();
+		def = v4l2Info.def().get<int32_t>();
+		max = v4l2Info.max().get<int32_t>();
+	} else if (v4l2Info.min().type() == ControlTypeBool) {
+		min = v4l2Info.min().get<bool>();
+		def = v4l2Info.def().get<bool>();
+		max = v4l2Info.max().get<bool>();
+	} else {
+		LOG(UVC, Error) << "v4l2 ControlInfo with value type "
+				<< v4l2Info.min().type() << " not mapped";
+		return;
+	}
 
 	switch (cid) {
 	case V4L2_CID_BRIGHTNESS: {
@@ -739,6 +791,47 @@  void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 		break;
 	}
 
+	case V4L2_CID_FOCUS_ABSOLUTE: {
+		/*
+		 * LensPosition expects values to be in the range of
+		 * [0.0f, maxDioptres], while a value of 0 means focused to
+		 * infinity, 0.5 means focused at 2m, 2 means focused at 50cm
+		 * and maxDioptres will be the closest possible focus which
+		 * will equate to a focus distance of (1 / maxDioptres) metres.
+		 *
+		 * \todo These values will definitely vary for each different
+		 * hardware, so this should be tunable somehow. In this case
+		 * 0.15f (~= 150 / (1023 - 1)) was a value read from
+		 * V4L2_CID_FOCUS_ABSOLUTE control when using a random camera
+		 * and having it autofocus at a distance of about 50cm.
+		 * This means the minimum focus distance of this camera should
+		 * be 75mm (0.15 / 2) which equals a maxDioptres value of ~=
+		 * 13.3333 (2 / 0.15). Also, the values might not scale
+		 * linearly, but this implementation assumes they do.
+		 */
+		float focusedAt50Cm = 0.15f * (max - min);
+		float scale = 2.0f / focusedAt50Cm;
+
+		info = ControlInfo{
+			{ 0.0f },
+			{ (max - min) * scale },
+			{ (def - min) * scale }
+		};
+		break;
+	}
+
+	case V4L2_CID_FOCUS_AUTO: {
+		std::vector<ControlValue> values;
+
+		if (!min || !max)
+			values.emplace_back(static_cast<int32_t>(controls::AfModeManual));
+		if (min || max)
+			values.emplace_back(static_cast<int32_t>(controls::AfModeContinuous));
+
+		info = ControlInfo{ values, values.back() };
+		break;
+	}
+
 	default:
 		info = v4l2Info;
 		break;