[libcamera-devel,v7,1/5] libcamera: v4l2_subdevice: Implement ENUM_FRAME_SIZES

Message ID 20190301154103.19588-2-jacopo@jmondi.org
State Accepted
Commit 0559c740f201bebf89983464c2e9ae92d0a8936b
Headers show
Series
  • v4l2_(sub)dev: improvements and tests
Related show

Commit Message

Jacopo Mondi March 1, 2019, 3:40 p.m. UTC
Implement format and size enumeration methods to list all the available
subdevice image resolutions and formats.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 src/libcamera/geometry.cpp             | 34 ++++++++++
 src/libcamera/include/geometry.h       | 12 ++++
 src/libcamera/include/v4l2_subdevice.h | 11 +++-
 src/libcamera/v4l2_subdevice.cpp       | 87 ++++++++++++++++++++++++++
 4 files changed, 142 insertions(+), 2 deletions(-)

Patch

diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index 57f4fc7716d9..b6b6592bdfec 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -46,4 +46,38 @@  namespace libcamera {
  * \brief The distance between the top and bottom sides
  */
 
+/**
+ * \struct SizeRange
+ * \brief Describe a range of image sizes
+ *
+ * SizeRange describes a range of image sizes included in the (minWidth,
+ * minHeight) - (maxWidth, maxHeight) interval. If the minimum and
+ * maximum sizes are identical it represents a single image resolution.
+ */
+
+/**
+ * \fn SizeRange::SizeRange()
+ * \brief Construct a size range
+ */
+
+/**
+ * \var SizeRange::minWidth
+ * \brief The minimum image width
+ */
+
+/**
+ * \var SizeRange::minHeight
+ * \brief The minimum image height
+ */
+
+/**
+ * \var SizeRange::maxWidth
+ * \brief The maximum image width
+ */
+
+/**
+ * \var SizeRange::maxHeight
+ * \brief The maximum image height
+ */
+
 } /* namespace libcamera */
diff --git a/src/libcamera/include/geometry.h b/src/libcamera/include/geometry.h
index cc146da7cb0d..eadc4ed4f9cb 100644
--- a/src/libcamera/include/geometry.h
+++ b/src/libcamera/include/geometry.h
@@ -17,6 +17,18 @@  struct Rectangle {
 	unsigned int h;
 };
 
+struct SizeRange {
+	SizeRange(unsigned int minW, unsigned int minH,
+		  unsigned int maxW, unsigned int maxH)
+		: minWidth(minW), minHeight(minH), maxWidth(maxW),
+		  maxHeight(maxH) {}
+
+	unsigned int minWidth;
+	unsigned int minHeight;
+	unsigned int maxWidth;
+	unsigned int maxHeight;
+};
+
 } /* namespace libcamera */
 
 #endif /* __LIBCAMERA_GEOMETRY_H__ */
diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
index 669e79f9a9fd..1cc0fab73103 100644
--- a/src/libcamera/include/v4l2_subdevice.h
+++ b/src/libcamera/include/v4l2_subdevice.h
@@ -7,15 +7,16 @@ 
 #ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__
 #define __LIBCAMERA_V4L2_SUBDEVICE_H__
 
+#include <map>
 #include <string>
+#include <vector>
 
+#include "geometry.h"
 #include "log.h"
 #include "media_object.h"
 
 namespace libcamera {
 
-struct Rectangle;
-
 struct V4L2SubdeviceFormat {
 	uint32_t mbus_code;
 	uint32_t width;
@@ -39,6 +40,9 @@  public:
 	int setCrop(unsigned int pad, Rectangle *rect);
 	int setCompose(unsigned int pad, Rectangle *rect);
 
+	const std::map<unsigned int, std::vector<SizeRange>>
+						formats(unsigned int pad);
+
 	int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
 	int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
 
@@ -46,6 +50,9 @@  protected:
 	std::string logPrefix() const;
 
 private:
+	int enumPadSizes(unsigned int pad, unsigned int code,
+			 std::vector<SizeRange> *size);
+
 	int setSelection(unsigned int pad, unsigned int target,
 			 Rectangle *rect);
 
diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
index 87377762664d..394ed0f061f3 100644
--- a/src/libcamera/v4l2_subdevice.cpp
+++ b/src/libcamera/v4l2_subdevice.cpp
@@ -183,6 +183,59 @@  int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect)
 	return setSelection(pad, V4L2_SEL_TGT_COMPOSE, rect);
 }
 
+/**
+ * \brief List the sub-device image resolutions and formats on \a pad
+ * \param[in] pad The 0-indexed pad number to enumerate formats on
+ *
+ * Retrieve a list of image formats and sizes on the \a pad of a video
+ * subdevice. Subdevices can report either a list of discrete sizes they
+ * support or a list of intervals expressed as a [min-max] sizes range.
+ *
+ * Each image size list is associated with a media bus pixel code for which
+ * the reported resolutions are supported.
+ *
+ * \return A map of image formats associated with a list of image sizes, or
+ * an empty map on error or if the pad does not exist
+ */
+const std::map<unsigned int, std::vector<SizeRange>>
+V4L2Subdevice::formats(unsigned int pad)
+{
+	std::map<unsigned int, std::vector<SizeRange>> formatMap = {};
+	struct v4l2_subdev_mbus_code_enum mbusEnum = {};
+	int ret;
+
+	if (pad >= entity_->pads().size()) {
+		LOG(V4L2Subdev, Error) << "Invalid pad: " << pad;
+		return formatMap;
+	}
+
+	mbusEnum.pad = pad;
+	mbusEnum.index = 0;
+	mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	while (true) {
+		ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);
+		if (ret)
+			break;
+
+		ret = enumPadSizes(pad, mbusEnum.code,
+				   &formatMap[mbusEnum.code]);
+		if (ret)
+			break;
+
+		mbusEnum.index++;
+	}
+
+	if (ret && (errno != EINVAL && errno != ENOTTY)) {
+		ret = -errno;
+		LOG(V4L2Subdev, Error)
+			<< "Unable to enumerate formats on pad " << pad
+			<< ": " << strerror(-ret);
+		formatMap.clear();
+	}
+
+	return formatMap;
+}
+
 /**
  * \brief Retrieve the image format set on one of the V4L2 subdevice pads
  * \param[in] pad The 0-indexed pad number the format is to be retrieved from
@@ -248,6 +301,40 @@  int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
 	return 0;
 }
 
+int V4L2Subdevice::enumPadSizes(unsigned int pad,unsigned int code,
+				std::vector<SizeRange> *sizes)
+{
+	struct v4l2_subdev_frame_size_enum sizeEnum = {};
+	int ret;
+
+	sizeEnum.index = 0;
+	sizeEnum.pad = pad;
+	sizeEnum.code = code;
+	sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	while (true) {
+		ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);
+		if (ret)
+			break;
+
+		sizes->emplace_back(sizeEnum.min_width, sizeEnum.min_height,
+				    sizeEnum.max_width, sizeEnum.max_height);
+
+		sizeEnum.index++;
+	}
+
+	if (ret && (errno != EINVAL && errno != ENOTTY)) {
+		ret = -errno;
+		LOG(V4L2Subdev, Error)
+			<< "Unable to enumerate sizes on pad " << pad
+			<< ": " << strerror(-ret);
+		sizes->clear();
+
+		return ret;
+	}
+
+	return 0;
+}
+
 int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
 				Rectangle *rect)
 {