[libcamera-devel,v3,04/22] libcamera: V4L2VideoDevice: Add tryFormat

Message ID 20200704133140.1738660-5-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • Clean up formats in v4l2-compat and pipeline handlers
Related show

Commit Message

Paul Elder July 4, 2020, 1:31 p.m. UTC
Add tryFormat and its variations (meta, single-plane, multi-plane) to
V4L2VideoDevice.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
New in v3
---
 include/libcamera/internal/v4l2_videodevice.h |   4 +
 src/libcamera/v4l2_videodevice.cpp            | 121 ++++++++++++++++++
 2 files changed, 125 insertions(+)

Comments

Laurent Pinchart July 4, 2020, 6:15 p.m. UTC | #1
Hi Paul,

Thank you for the patch.

On Sat, Jul 04, 2020 at 10:31:22PM +0900, Paul Elder wrote:
> Add tryFormat and its variations (meta, single-plane, multi-plane) to
> V4L2VideoDevice.
> 
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> ---
> New in v3
> ---
>  include/libcamera/internal/v4l2_videodevice.h |   4 +
>  src/libcamera/v4l2_videodevice.cpp            | 121 ++++++++++++++++++
>  2 files changed, 125 insertions(+)
> 
> diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
> index 4d21f5a..56c3aee 100644
> --- a/include/libcamera/internal/v4l2_videodevice.h
> +++ b/include/libcamera/internal/v4l2_videodevice.h
> @@ -186,6 +186,7 @@ public:
>  	const V4L2Capability &caps() const { return caps_; }
>  
>  	int getFormat(V4L2DeviceFormat *format);
> +	int tryFormat(V4L2DeviceFormat *format);
>  	int setFormat(V4L2DeviceFormat *format);
>  	std::map<V4L2PixelFormat, std::vector<SizeRange>> formats(uint32_t code = 0);
>  
> @@ -217,12 +218,15 @@ protected:
>  
>  private:
>  	int getFormatMeta(V4L2DeviceFormat *format);
> +	int tryFormatMeta(V4L2DeviceFormat *format);
>  	int setFormatMeta(V4L2DeviceFormat *format);
>  
>  	int getFormatMultiplane(V4L2DeviceFormat *format);
> +	int tryFormatMultiplane(V4L2DeviceFormat *format);
>  	int setFormatMultiplane(V4L2DeviceFormat *format);
>  
>  	int getFormatSingleplane(V4L2DeviceFormat *format);
> +	int tryFormatSingleplane(V4L2DeviceFormat *format);
>  	int setFormatSingleplane(V4L2DeviceFormat *format);
>  
>  	std::vector<V4L2PixelFormat> enumPixelformats(uint32_t code);
> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> index 3614b2e..f25914c 100644
> --- a/src/libcamera/v4l2_videodevice.cpp
> +++ b/src/libcamera/v4l2_videodevice.cpp
> @@ -723,6 +723,26 @@ int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format)
>  		return getFormatSingleplane(format);
>  }
>  
> +/**
> + * \brief Try an image format on the V4L2 video device
> + * \param[inout] format The image format to test applicablity to the video device

s/applicablity/applicability/

> + *
> + * Test if the supplied \a format to the video device is acceptable, and return
> + * the actually applied format parameters, as \ref V4L2VideoDevice::tryFormat

This is the documentation of V4L2VideoDevice::tryFormat... Did you mean
setFormat() ? You shouldn't need \ref or the V4L2VideoDevice prefix,
writing setFormat() should generate the right link.

Also, the parameters are not "applied". How about

 * Try the supplied \a format on the video device without applying it, returning
 * the format that would be applied. This is equivalent to setFormat(), except
 * that the device configuration is not changed.

> + * would do.
> + *
> + * \return 0 on success or a negative error code otherwise
> + */
> +int V4L2VideoDevice::tryFormat(V4L2DeviceFormat *format)
> +{
> +	if (caps_.isMeta())
> +		return tryFormatMeta(format);
> +	else if (caps_.isMultiplanar())
> +		return tryFormatMultiplane(format);
> +	else
> +		return tryFormatSingleplane(format);
> +}
> +
>  /**
>   * \brief Configure an image format on the V4L2 video device
>   * \param[inout] format The image format to apply to the video device
> @@ -765,6 +785,35 @@ int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)
>  	return 0;
>  }
>  
> +int V4L2VideoDevice::tryFormatMeta(V4L2DeviceFormat *format)
> +{
> +	struct v4l2_format v4l2Format = {};
> +	struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
> +	int ret;
> +
> +	v4l2Format.type = bufferType_;
> +	pix->dataformat = format->fourcc;
> +	pix->buffersize = format->planes[0].size;
> +	ret = ioctl(VIDIOC_TRY_FMT, &v4l2Format);
> +	if (ret) {
> +		LOG(V4L2, Error) << "Unable to try format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * Return to caller the format actually applied on the video device,
> +	 * which might differ from the requested one.
> +	 */
> +	format->size.width = 0;
> +	format->size.height = 0;
> +	format->fourcc = V4L2PixelFormat(pix->dataformat);
> +	format->planesCount = 1;
> +	format->planes[0].bpl = pix->buffersize;
> +	format->planes[0].size = pix->buffersize;
> +
> +	return 0;
> +}
> +

This is duplicating quite a bit of code. How about turning the existing
setFormatMeta(), setFormatMultiplane() and setFormatSinglePlane()
functions into trySetFormat*(), with a parameter to tell whether to try
the format or set it (can be a "bool set" as these are private
functions, if it was a public API an enum would be better) ?

As the error message is also different (the functions here print "Unable
to try format"), if you want to avoid a	(set ? "set" : "try")
conditional, you can move the error messages from the low-level
functions to setFormat() and tryFormat(). Up to you.

With these changes,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

>  int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)
>  {
>  	struct v4l2_format v4l2Format = {};
> @@ -820,6 +869,46 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)
>  	return 0;
>  }
>  
> +int V4L2VideoDevice::tryFormatMultiplane(V4L2DeviceFormat *format)
> +{
> +	struct v4l2_format v4l2Format = {};
> +	struct v4l2_pix_format_mplane *pix = &v4l2Format.fmt.pix_mp;
> +	int ret;
> +
> +	v4l2Format.type = bufferType_;
> +	pix->width = format->size.width;
> +	pix->height = format->size.height;
> +	pix->pixelformat = format->fourcc;
> +	pix->num_planes = format->planesCount;
> +	pix->field = V4L2_FIELD_NONE;
> +
> +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> +		pix->plane_fmt[i].bytesperline = format->planes[i].bpl;
> +		pix->plane_fmt[i].sizeimage = format->planes[i].size;
> +	}
> +
> +	ret = ioctl(VIDIOC_TRY_FMT, &v4l2Format);
> +	if (ret) {
> +		LOG(V4L2, Error) << "Unable to try format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * Return to caller the format actually applied on the video device,
> +	 * which might differ from the requested one.
> +	 */
> +	format->size.width = pix->width;
> +	format->size.height = pix->height;
> +	format->fourcc = V4L2PixelFormat(pix->pixelformat);
> +	format->planesCount = pix->num_planes;
> +	for (unsigned int i = 0; i < format->planesCount; ++i) {
> +		format->planes[i].bpl = pix->plane_fmt[i].bytesperline;
> +		format->planes[i].size = pix->plane_fmt[i].sizeimage;
> +	}
> +
> +	return 0;
> +}
> +
>  int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)
>  {
>  	struct v4l2_format v4l2Format = {};
> @@ -883,6 +972,38 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)
>  	return 0;
>  }
>  
> +int V4L2VideoDevice::tryFormatSingleplane(V4L2DeviceFormat *format)
> +{
> +	struct v4l2_format v4l2Format = {};
> +	struct v4l2_pix_format *pix = &v4l2Format.fmt.pix;
> +	int ret;
> +
> +	v4l2Format.type = bufferType_;
> +	pix->width = format->size.width;
> +	pix->height = format->size.height;
> +	pix->pixelformat = format->fourcc;
> +	pix->bytesperline = format->planes[0].bpl;
> +	pix->field = V4L2_FIELD_NONE;
> +	ret = ioctl(VIDIOC_TRY_FMT, &v4l2Format);
> +	if (ret) {
> +		LOG(V4L2, Error) << "Unable to try format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	/*
> +	 * Return to caller the format actually applied on the device,
> +	 * which might differ from the requested one.
> +	 */
> +	format->size.width = pix->width;
> +	format->size.height = pix->height;
> +	format->fourcc = V4L2PixelFormat(pix->pixelformat);
> +	format->planesCount = 1;
> +	format->planes[0].bpl = pix->bytesperline;
> +	format->planes[0].size = pix->sizeimage;
> +
> +	return 0;
> +}
> +
>  int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)
>  {
>  	struct v4l2_format v4l2Format = {};

Patch

diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h
index 4d21f5a..56c3aee 100644
--- a/include/libcamera/internal/v4l2_videodevice.h
+++ b/include/libcamera/internal/v4l2_videodevice.h
@@ -186,6 +186,7 @@  public:
 	const V4L2Capability &caps() const { return caps_; }
 
 	int getFormat(V4L2DeviceFormat *format);
+	int tryFormat(V4L2DeviceFormat *format);
 	int setFormat(V4L2DeviceFormat *format);
 	std::map<V4L2PixelFormat, std::vector<SizeRange>> formats(uint32_t code = 0);
 
@@ -217,12 +218,15 @@  protected:
 
 private:
 	int getFormatMeta(V4L2DeviceFormat *format);
+	int tryFormatMeta(V4L2DeviceFormat *format);
 	int setFormatMeta(V4L2DeviceFormat *format);
 
 	int getFormatMultiplane(V4L2DeviceFormat *format);
+	int tryFormatMultiplane(V4L2DeviceFormat *format);
 	int setFormatMultiplane(V4L2DeviceFormat *format);
 
 	int getFormatSingleplane(V4L2DeviceFormat *format);
+	int tryFormatSingleplane(V4L2DeviceFormat *format);
 	int setFormatSingleplane(V4L2DeviceFormat *format);
 
 	std::vector<V4L2PixelFormat> enumPixelformats(uint32_t code);
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 3614b2e..f25914c 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -723,6 +723,26 @@  int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format)
 		return getFormatSingleplane(format);
 }
 
+/**
+ * \brief Try an image format on the V4L2 video device
+ * \param[inout] format The image format to test applicablity to the video device
+ *
+ * Test if the supplied \a format to the video device is acceptable, and return
+ * the actually applied format parameters, as \ref V4L2VideoDevice::tryFormat
+ * would do.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int V4L2VideoDevice::tryFormat(V4L2DeviceFormat *format)
+{
+	if (caps_.isMeta())
+		return tryFormatMeta(format);
+	else if (caps_.isMultiplanar())
+		return tryFormatMultiplane(format);
+	else
+		return tryFormatSingleplane(format);
+}
+
 /**
  * \brief Configure an image format on the V4L2 video device
  * \param[inout] format The image format to apply to the video device
@@ -765,6 +785,35 @@  int V4L2VideoDevice::getFormatMeta(V4L2DeviceFormat *format)
 	return 0;
 }
 
+int V4L2VideoDevice::tryFormatMeta(V4L2DeviceFormat *format)
+{
+	struct v4l2_format v4l2Format = {};
+	struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
+	int ret;
+
+	v4l2Format.type = bufferType_;
+	pix->dataformat = format->fourcc;
+	pix->buffersize = format->planes[0].size;
+	ret = ioctl(VIDIOC_TRY_FMT, &v4l2Format);
+	if (ret) {
+		LOG(V4L2, Error) << "Unable to try format: " << strerror(-ret);
+		return ret;
+	}
+
+	/*
+	 * Return to caller the format actually applied on the video device,
+	 * which might differ from the requested one.
+	 */
+	format->size.width = 0;
+	format->size.height = 0;
+	format->fourcc = V4L2PixelFormat(pix->dataformat);
+	format->planesCount = 1;
+	format->planes[0].bpl = pix->buffersize;
+	format->planes[0].size = pix->buffersize;
+
+	return 0;
+}
+
 int V4L2VideoDevice::setFormatMeta(V4L2DeviceFormat *format)
 {
 	struct v4l2_format v4l2Format = {};
@@ -820,6 +869,46 @@  int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format)
 	return 0;
 }
 
+int V4L2VideoDevice::tryFormatMultiplane(V4L2DeviceFormat *format)
+{
+	struct v4l2_format v4l2Format = {};
+	struct v4l2_pix_format_mplane *pix = &v4l2Format.fmt.pix_mp;
+	int ret;
+
+	v4l2Format.type = bufferType_;
+	pix->width = format->size.width;
+	pix->height = format->size.height;
+	pix->pixelformat = format->fourcc;
+	pix->num_planes = format->planesCount;
+	pix->field = V4L2_FIELD_NONE;
+
+	for (unsigned int i = 0; i < pix->num_planes; ++i) {
+		pix->plane_fmt[i].bytesperline = format->planes[i].bpl;
+		pix->plane_fmt[i].sizeimage = format->planes[i].size;
+	}
+
+	ret = ioctl(VIDIOC_TRY_FMT, &v4l2Format);
+	if (ret) {
+		LOG(V4L2, Error) << "Unable to try format: " << strerror(-ret);
+		return ret;
+	}
+
+	/*
+	 * Return to caller the format actually applied on the video device,
+	 * which might differ from the requested one.
+	 */
+	format->size.width = pix->width;
+	format->size.height = pix->height;
+	format->fourcc = V4L2PixelFormat(pix->pixelformat);
+	format->planesCount = pix->num_planes;
+	for (unsigned int i = 0; i < format->planesCount; ++i) {
+		format->planes[i].bpl = pix->plane_fmt[i].bytesperline;
+		format->planes[i].size = pix->plane_fmt[i].sizeimage;
+	}
+
+	return 0;
+}
+
 int V4L2VideoDevice::setFormatMultiplane(V4L2DeviceFormat *format)
 {
 	struct v4l2_format v4l2Format = {};
@@ -883,6 +972,38 @@  int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format)
 	return 0;
 }
 
+int V4L2VideoDevice::tryFormatSingleplane(V4L2DeviceFormat *format)
+{
+	struct v4l2_format v4l2Format = {};
+	struct v4l2_pix_format *pix = &v4l2Format.fmt.pix;
+	int ret;
+
+	v4l2Format.type = bufferType_;
+	pix->width = format->size.width;
+	pix->height = format->size.height;
+	pix->pixelformat = format->fourcc;
+	pix->bytesperline = format->planes[0].bpl;
+	pix->field = V4L2_FIELD_NONE;
+	ret = ioctl(VIDIOC_TRY_FMT, &v4l2Format);
+	if (ret) {
+		LOG(V4L2, Error) << "Unable to try format: " << strerror(-ret);
+		return ret;
+	}
+
+	/*
+	 * Return to caller the format actually applied on the device,
+	 * which might differ from the requested one.
+	 */
+	format->size.width = pix->width;
+	format->size.height = pix->height;
+	format->fourcc = V4L2PixelFormat(pix->pixelformat);
+	format->planesCount = 1;
+	format->planes[0].bpl = pix->bytesperline;
+	format->planes[0].size = pix->sizeimage;
+
+	return 0;
+}
+
 int V4L2VideoDevice::setFormatSingleplane(V4L2DeviceFormat *format)
 {
 	struct v4l2_format v4l2Format = {};