[libcamera-devel,v2,2/6] libcamera: v4l2_subdevice: Implement ENUM_FRAME_SIZES

Message ID 20190225121037.11415-3-jacopo@jmondi.org
State Superseded
Headers show
Series
  • v4l2_(sub)dev: improvements and tests
Related show

Commit Message

Jacopo Mondi Feb. 25, 2019, 12:10 p.m. UTC
Implement enumFormat() methods to enumerate the available image
resolutions on the subdevice.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 src/libcamera/include/v4l2_subdevice.h |  8 +++
 src/libcamera/v4l2_subdevice.cpp       | 93 ++++++++++++++++++++++++++
 2 files changed, 101 insertions(+)

Comments

Niklas Söderlund Feb. 26, 2019, 2:39 a.m. UTC | #1
Hi Jacopo,

Thanks for your patch.

On 2019-02-25 13:10:33 +0100, Jacopo Mondi wrote:
> Implement enumFormat() methods to enumerate the available image
> resolutions on the subdevice.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
>  src/libcamera/include/v4l2_subdevice.h |  8 +++
>  src/libcamera/v4l2_subdevice.cpp       | 93 ++++++++++++++++++++++++++
>  2 files changed, 101 insertions(+)
> 
> diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
> index fcfbee5af106..cb033a76933c 100644
> --- a/src/libcamera/include/v4l2_subdevice.h
> +++ b/src/libcamera/include/v4l2_subdevice.h
> @@ -7,7 +7,9 @@
>  #ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__
>  #define __LIBCAMERA_V4L2_SUBDEVICE_H__
>  
> +#include <map>
>  #include <string>
> +#include <vector>
>  
>  #include "media_object.h"
>  
> @@ -38,16 +40,22 @@ public:
>  	int setCrop(unsigned int pad, Rectangle *rect);
>  	int setCompose(unsigned int pad, Rectangle *rect);
>  
> +	std::vector<V4L2SubdeviceFormat> &formats(unsigned int pad);

As you return a referent to the internal data structure should it not 
at lest be const?

>  	int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
>  	int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
>  
>  private:
> +	int listPadFormats(unsigned int pad,
> +			   std::vector<V4L2SubdeviceFormat> *formats);
> +	void listFormats();
>  	int setSelection(unsigned int pad, unsigned int target,
>  			 Rectangle *rect);
>  
>  	const MediaEntity *entity_;
>  	std::string deviceNode_;
>  	int fd_;
> +
> +	std::map<unsigned int, std::vector<V4L2SubdeviceFormat>> formats_;
>  };
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
> index ebf87f0124cb..0e9c654579dc 100644
> --- a/src/libcamera/v4l2_subdevice.cpp
> +++ b/src/libcamera/v4l2_subdevice.cpp
> @@ -5,6 +5,10 @@
>   * v4l2_subdevice.cpp - V4L2 Subdevice
>   */
>  
> +#include <map>
> +#include <string>
> +#include <vector>
> +
>  #include <fcntl.h>
>  #include <string.h>
>  #include <sys/ioctl.h>
> @@ -116,6 +120,8 @@ int V4L2Subdevice::open()
>  	}
>  	fd_ = ret;
>  
> +	listFormats();
> +

I would inline the function here, but I won't push it as its mostly 
taste.

>  	return 0;
>  }
>  
> @@ -178,6 +184,17 @@ 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
> + *
> + * \return A vector of image formats, or an empty vector on error
> + */
> +std::vector<V4L2SubdeviceFormat> &V4L2Subdevice::formats(unsigned int pad)
> +{
> +	return formats_[pad];

Maybe add a bounds checking on pad here? What would happen if a pad that 
don't exists is requested?

> +}
> +
>  /**
>   * \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
> @@ -242,6 +259,82 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
>  	return 0;
>  }
>  
> +int V4L2Subdevice::listPadFormats(unsigned int pad,
> +				  std::vector<V4L2SubdeviceFormat> *formats)
> +{
> +	struct v4l2_subdev_frame_size_enum sizeEnum = {};
> +	struct v4l2_subdev_mbus_code_enum mbusEnum = {};
> +	int ret;
> +
> +	mbusEnum.index = 0;
> +	mbusEnum.pad = pad;
> +	mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +
> +	while (!(ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum))) {
> +		sizeEnum.index = 0;
> +		sizeEnum.code = mbusEnum.code;
> +		sizeEnum.pad = pad;
> +		sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> +
> +		while (!(ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE,
> +				     &sizeEnum))) {
> +
> +			/* Store the minimum and maximum reported sizes. */
> +			V4L2SubdeviceFormat minFormat = {
> +				.mbus_code = mbusEnum.code,
> +				.width = sizeEnum.min_width,
> +				.height = sizeEnum.min_height,
> +			};
> +			formats->push_back(minFormat);
> +
> +			V4L2SubdeviceFormat maxFormat = {
> +				.mbus_code = mbusEnum.code,
> +				.width = sizeEnum.max_width,
> +				.height = sizeEnum.max_height,
> +			};
> +			formats->push_back(maxFormat);
> +
> +			sizeEnum.index++;
> +		}
> +
> +		if (-errno != -EINVAL) {
> +			LOG(V4L2Subdev, Error)
> +				<< "Unable to enumerate format on pad " << pad
> +				<< " of " << deviceNode_ << ": "
> +				<< strerror(-ret);
> +			return ret;
> +		}
> +
> +		mbusEnum.index++;
> +	}
> +
> +	if (-errno != -EINVAL) {
> +		LOG(V4L2Subdev, Error)
> +			<< "Unable to enumerate format on pad " << pad
> +			<< " of " << deviceNode_ << ": " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +void V4L2Subdevice::listFormats()
> +{
> +	int ret;
> +
> +	for (MediaPad *pad : entity_->pads()) {
> +		std::vector<V4L2SubdeviceFormat> formats = {};
> +		ret = listPadFormats(pad->index(), &formats);

Can't listPadFormats() return the vector instead of taking a pointer as 
an argument? With copy elision there would be no performance impact.

> +		if (ret) {
> +			formats = {};
> +			formats_[pad->index()] = formats;
> +			continue;
> +		}
> +
> +		formats_[pad->index()] = formats;

This can be simplified, how about

    for (MediaPad *pad : entity_->pads()) {
        std::vector<V4L2SubdeviceFormat> formats = {};
        ret = listPadFormats(pad->index(), &formats);
        if (ret)
            formats = {}

        formats_[pad->index()] = formats;
    }


Or with the return type of listPadFormats() changed

    for (MediaPad *pad : entity_->pads())
        formats_[pad->index()] = listPadFormats(pad->index());


> +	}
> +}
> +
>  int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
>  				Rectangle *rect)
>  {
> -- 
> 2.20.1
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Jacopo Mondi Feb. 26, 2019, 8:27 a.m. UTC | #2
Hi Niklas,
   thanks for review.

On Tue, Feb 26, 2019 at 03:39:42AM +0100, Niklas Söderlund wrote:
> Hi Jacopo,
>
> Thanks for your patch.
>
> On 2019-02-25 13:10:33 +0100, Jacopo Mondi wrote:
> > Implement enumFormat() methods to enumerate the available image
> > resolutions on the subdevice.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > ---
> >  src/libcamera/include/v4l2_subdevice.h |  8 +++
> >  src/libcamera/v4l2_subdevice.cpp       | 93 ++++++++++++++++++++++++++
> >  2 files changed, 101 insertions(+)
> >
> > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
> > index fcfbee5af106..cb033a76933c 100644
> > --- a/src/libcamera/include/v4l2_subdevice.h
> > +++ b/src/libcamera/include/v4l2_subdevice.h
> > @@ -7,7 +7,9 @@
> >  #ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__
> >  #define __LIBCAMERA_V4L2_SUBDEVICE_H__
> >
> > +#include <map>
> >  #include <string>
> > +#include <vector>
> >
> >  #include "media_object.h"
> >
> > @@ -38,16 +40,22 @@ public:
> >  	int setCrop(unsigned int pad, Rectangle *rect);
> >  	int setCompose(unsigned int pad, Rectangle *rect);
> >
> > +	std::vector<V4L2SubdeviceFormat> &formats(unsigned int pad);
>
> As you return a referent to the internal data structure should it not
> at lest be const?

Possibly, yes. I'll try to make it const.

>
> >  	int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
> >  	int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
> >
> >  private:
> > +	int listPadFormats(unsigned int pad,
> > +			   std::vector<V4L2SubdeviceFormat> *formats);
> > +	void listFormats();
> >  	int setSelection(unsigned int pad, unsigned int target,
> >  			 Rectangle *rect);
> >
> >  	const MediaEntity *entity_;
> >  	std::string deviceNode_;
> >  	int fd_;
> > +
> > +	std::map<unsigned int, std::vector<V4L2SubdeviceFormat>> formats_;
> >  };
> >
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
> > index ebf87f0124cb..0e9c654579dc 100644
> > --- a/src/libcamera/v4l2_subdevice.cpp
> > +++ b/src/libcamera/v4l2_subdevice.cpp
> > @@ -5,6 +5,10 @@
> >   * v4l2_subdevice.cpp - V4L2 Subdevice
> >   */
> >
> > +#include <map>
> > +#include <string>
> > +#include <vector>
> > +
> >  #include <fcntl.h>
> >  #include <string.h>
> >  #include <sys/ioctl.h>
> > @@ -116,6 +120,8 @@ int V4L2Subdevice::open()
> >  	}
> >  	fd_ = ret;
> >
> > +	listFormats();
> > +
>
> I would inline the function here, but I won't push it as its mostly
> taste.
>

Actually not sure I like it.

> >  	return 0;
> >  }
> >
> > @@ -178,6 +184,17 @@ 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
> > + *
> > + * \return A vector of image formats, or an empty vector on error
> > + */
> > +std::vector<V4L2SubdeviceFormat> &V4L2Subdevice::formats(unsigned int pad)
> > +{
> > +	return formats_[pad];
>
> Maybe add a bounds checking on pad here? What would happen if a pad that
> don't exists is requested?
>

My understanding is that an empty vector is constructed and returned
in such a case (and gets stored in the formats map, this is a side
effect we might want to avoid maybe).

> > +}
> > +
> >  /**
> >   * \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
> > @@ -242,6 +259,82 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> >  	return 0;
> >  }
> >
> > +int V4L2Subdevice::listPadFormats(unsigned int pad,
> > +				  std::vector<V4L2SubdeviceFormat> *formats)
> > +{
> > +	struct v4l2_subdev_frame_size_enum sizeEnum = {};
> > +	struct v4l2_subdev_mbus_code_enum mbusEnum = {};
> > +	int ret;
> > +
> > +	mbusEnum.index = 0;
> > +	mbusEnum.pad = pad;
> > +	mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > +
> > +	while (!(ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum))) {
> > +		sizeEnum.index = 0;
> > +		sizeEnum.code = mbusEnum.code;
> > +		sizeEnum.pad = pad;
> > +		sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > +
> > +		while (!(ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE,
> > +				     &sizeEnum))) {
> > +
> > +			/* Store the minimum and maximum reported sizes. */
> > +			V4L2SubdeviceFormat minFormat = {
> > +				.mbus_code = mbusEnum.code,
> > +				.width = sizeEnum.min_width,
> > +				.height = sizeEnum.min_height,
> > +			};
> > +			formats->push_back(minFormat);
> > +
> > +			V4L2SubdeviceFormat maxFormat = {
> > +				.mbus_code = mbusEnum.code,
> > +				.width = sizeEnum.max_width,
> > +				.height = sizeEnum.max_height,
> > +			};
> > +			formats->push_back(maxFormat);
> > +
> > +			sizeEnum.index++;
> > +		}
> > +
> > +		if (-errno != -EINVAL) {
> > +			LOG(V4L2Subdev, Error)
> > +				<< "Unable to enumerate format on pad " << pad
> > +				<< " of " << deviceNode_ << ": "
> > +				<< strerror(-ret);
> > +			return ret;
> > +		}
> > +
> > +		mbusEnum.index++;
> > +	}
> > +
> > +	if (-errno != -EINVAL) {
> > +		LOG(V4L2Subdev, Error)
> > +			<< "Unable to enumerate format on pad " << pad
> > +			<< " of " << deviceNode_ << ": " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +void V4L2Subdevice::listFormats()
> > +{
> > +	int ret;
> > +
> > +	for (MediaPad *pad : entity_->pads()) {
> > +		std::vector<V4L2SubdeviceFormat> formats = {};
> > +		ret = listPadFormats(pad->index(), &formats);
>
> Can't listPadFormats() return the vector instead of taking a pointer as
> an argument? With copy elision there would be no performance impact.
>
> > +		if (ret) {
> > +			formats = {};
> > +			formats_[pad->index()] = formats;
> > +			continue;
> > +		}
> > +
> > +		formats_[pad->index()] = formats;
>
> This can be simplified, how about
>
>     for (MediaPad *pad : entity_->pads()) {
>         std::vector<V4L2SubdeviceFormat> formats = {};
>         ret = listPadFormats(pad->index(), &formats);
>         if (ret)
>             formats = {}
>
>         formats_[pad->index()] = formats;
>     }
>
>
> Or with the return type of listPadFormats() changed
>
>     for (MediaPad *pad : entity_->pads())
>         formats_[pad->index()] = listPadFormats(pad->index());
>
>

Thanks for both suggestions, it looks better.

Anyway, as soon as I've run this code on a 'real' device, I realized
it needs to handle gracefully the case where that ioctl is not
implemented (-ENOTTY), so this code will likely be re-writted in v3.

Thanks
  j

> > +	}
> > +}
> > +
> >  int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
> >  				Rectangle *rect)
> >  {
> > --
> > 2.20.1
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
>
> --
> Regards,
> Niklas Söderlund

Patch

diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdevice.h
index fcfbee5af106..cb033a76933c 100644
--- a/src/libcamera/include/v4l2_subdevice.h
+++ b/src/libcamera/include/v4l2_subdevice.h
@@ -7,7 +7,9 @@ 
 #ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__
 #define __LIBCAMERA_V4L2_SUBDEVICE_H__
 
+#include <map>
 #include <string>
+#include <vector>
 
 #include "media_object.h"
 
@@ -38,16 +40,22 @@  public:
 	int setCrop(unsigned int pad, Rectangle *rect);
 	int setCompose(unsigned int pad, Rectangle *rect);
 
+	std::vector<V4L2SubdeviceFormat> &formats(unsigned int pad);
 	int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
 	int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
 
 private:
+	int listPadFormats(unsigned int pad,
+			   std::vector<V4L2SubdeviceFormat> *formats);
+	void listFormats();
 	int setSelection(unsigned int pad, unsigned int target,
 			 Rectangle *rect);
 
 	const MediaEntity *entity_;
 	std::string deviceNode_;
 	int fd_;
+
+	std::map<unsigned int, std::vector<V4L2SubdeviceFormat>> formats_;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
index ebf87f0124cb..0e9c654579dc 100644
--- a/src/libcamera/v4l2_subdevice.cpp
+++ b/src/libcamera/v4l2_subdevice.cpp
@@ -5,6 +5,10 @@ 
  * v4l2_subdevice.cpp - V4L2 Subdevice
  */
 
+#include <map>
+#include <string>
+#include <vector>
+
 #include <fcntl.h>
 #include <string.h>
 #include <sys/ioctl.h>
@@ -116,6 +120,8 @@  int V4L2Subdevice::open()
 	}
 	fd_ = ret;
 
+	listFormats();
+
 	return 0;
 }
 
@@ -178,6 +184,17 @@  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
+ *
+ * \return A vector of image formats, or an empty vector on error
+ */
+std::vector<V4L2SubdeviceFormat> &V4L2Subdevice::formats(unsigned int pad)
+{
+	return formats_[pad];
+}
+
 /**
  * \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
@@ -242,6 +259,82 @@  int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
 	return 0;
 }
 
+int V4L2Subdevice::listPadFormats(unsigned int pad,
+				  std::vector<V4L2SubdeviceFormat> *formats)
+{
+	struct v4l2_subdev_frame_size_enum sizeEnum = {};
+	struct v4l2_subdev_mbus_code_enum mbusEnum = {};
+	int ret;
+
+	mbusEnum.index = 0;
+	mbusEnum.pad = pad;
+	mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+	while (!(ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum))) {
+		sizeEnum.index = 0;
+		sizeEnum.code = mbusEnum.code;
+		sizeEnum.pad = pad;
+		sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+
+		while (!(ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE,
+				     &sizeEnum))) {
+
+			/* Store the minimum and maximum reported sizes. */
+			V4L2SubdeviceFormat minFormat = {
+				.mbus_code = mbusEnum.code,
+				.width = sizeEnum.min_width,
+				.height = sizeEnum.min_height,
+			};
+			formats->push_back(minFormat);
+
+			V4L2SubdeviceFormat maxFormat = {
+				.mbus_code = mbusEnum.code,
+				.width = sizeEnum.max_width,
+				.height = sizeEnum.max_height,
+			};
+			formats->push_back(maxFormat);
+
+			sizeEnum.index++;
+		}
+
+		if (-errno != -EINVAL) {
+			LOG(V4L2Subdev, Error)
+				<< "Unable to enumerate format on pad " << pad
+				<< " of " << deviceNode_ << ": "
+				<< strerror(-ret);
+			return ret;
+		}
+
+		mbusEnum.index++;
+	}
+
+	if (-errno != -EINVAL) {
+		LOG(V4L2Subdev, Error)
+			<< "Unable to enumerate format on pad " << pad
+			<< " of " << deviceNode_ << ": " << strerror(-ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void V4L2Subdevice::listFormats()
+{
+	int ret;
+
+	for (MediaPad *pad : entity_->pads()) {
+		std::vector<V4L2SubdeviceFormat> formats = {};
+		ret = listPadFormats(pad->index(), &formats);
+		if (ret) {
+			formats = {};
+			formats_[pad->index()] = formats;
+			continue;
+		}
+
+		formats_[pad->index()] = formats;
+	}
+}
+
 int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
 				Rectangle *rect)
 {