[libcamera-devel,2/2] libcamera: v4l2_device: Add methods to get/set format

Message ID 20190128151137.31075-3-jacopo@jmondi.org
State Accepted
Headers show
Series
  • V4L2Device: Add get/set format support
Related show

Commit Message

Jacopo Mondi Jan. 28, 2019, 3:11 p.m. UTC
Add methods to set and get the image format programmed on a V4L2Device
for both the single and multi planar use case.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 src/libcamera/include/v4l2_device.h |  10 +++
 src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)

Comments

Kieran Bingham Jan. 28, 2019, 3:42 p.m. UTC | #1
Hi Jacopo,

I only have variable/layout concerns here - which is certainly not a
blocker at the moment.

Also - if the outcome of the discussion below changes this patches
expectations - then we'll need to adapt other patches too - so that
would be a global patch fix at that point.

Thus I don't think that discussion blocks this patch.


On 28/01/2019 15:11, Jacopo Mondi wrote:
> Add methods to set and get the image format programmed on a V4L2Device
> for both the single and multi planar use case.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>


> ---
>  src/libcamera/include/v4l2_device.h |  10 +++
>  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
>  2 files changed, 138 insertions(+)
> 
> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> index c70959e..fe54220 100644
> --- a/src/libcamera/include/v4l2_device.h
> +++ b/src/libcamera/include/v4l2_device.h
> @@ -86,10 +86,20 @@ public:
>  	const char *deviceName() const { return caps_.card(); }
>  	const char *busName() const { return caps_.bus_info(); }
>  
> +	int format(V4L2DeviceFormat *fmt);
> +	int setFormat(V4L2DeviceFormat *fmt);
> +
>  private:
>  	std::string deviceNode_;
>  	int fd_;
>  	V4L2Capability caps_;
> +	enum v4l2_buf_type bufferType_;
> +
> +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> +
> +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> +	int setFormatMultiplane(V4L2DeviceFormat *fmt);


Can I confirm <or prompt discussion> on our class layouts?

Have we defined a layout anywhere?

Here this creates:


class ClassName
{
public:
	ClassName();
	~ClassName();
	void publicFunctions();

	int publicData;
	bool publicBool;

private:
	int privateData;
	bool privateBool;

	int privateFunctions();
	int morePrivateFunctions();
}

Which is:
	PublicFunctions
	PublicData
	PrivateData
	PrivateFunctions


So the 'data' both public and private is sandwiched in the middle.
I don't mind this - but in my mind I thought we were doing:


class ClassName
{
public:
	ClassName();
	~ClassName();
	void publicFunctions();

	int publicData;
	bool publicBool;

private:
	int privateFunctions();
	int morePrivateFunctions();

	int privateData;
	bool privateBool;
}

Which is:
	PublicFunctions
	PublicData
	PrivateFunctions
	PrivateData

so that the public, and private (or protected) sections follow the same
sequence?

Any comments from the team here?

>  };
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> index d6143f2..5c415d0 100644
> --- a/src/libcamera/v4l2_device.cpp
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -227,6 +227,15 @@ int V4L2Device::open()
>  		return -EINVAL;
>  	}
>  
> +	if (caps_.isCapture())
> +		bufferType_ = caps_.isMultiplanar()
> +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
> +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	else
> +		bufferType_ = caps_.isMultiplanar()
> +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +
>  	return 0;
>  }
>  
> @@ -269,4 +278,123 @@ void V4L2Device::close()
>   * \return The string containing the device location
>   */
>  
> +/**
> + * \brief Retrieve the image format set on the V4L2 device
> + * \return 0 for success, a negative error code otherwise
> + */
> +int V4L2Device::format(V4L2DeviceFormat *fmt)
> +{> +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :

I think the precedence is that the : would be below the ? here ?

But I'm doubting myself - so hopefully Laurent will chime in with his
preferences here.


If so perhaps we should set the following change in .clang-format:

-BreakBeforeTernaryOperators: false
+BreakBeforeTernaryOperators: true

which would produce the following:


+       return caps_.isMultiplanar() ? getFormatMultiplane(fmt)
+                                    : getFormatSingleplane(fmt);



It doesn't however align the ? with the = in the bufferType_ above:

              bufferType_ = caps_.isMultiplanar()
                          ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
                          : V4L2_BUF_TYPE_VIDEO_OUTPUT;
                                    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
                                    : V4L2_BUF_TYPE_VIDEO_OUTPUT;

So again - I think that's a non-blocker currently.


> +				       getFormatSingleplane(fmt);
> +}
> +
> +/**
> + * \brief Configure an image format on the V4L2 device
> + * \return 0 for success, a negative error code otherwise
> + */
> +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
> +{
> +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
> +				       setFormatSingleplane(fmt);
> +}
> +
> +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	fmt->width = pix->width;
> +	fmt->height = pix->height;
> +	fmt->fourcc = pix->pixelformat;
> +	fmt->planes = 1;
> +	fmt->planesFmt[0].bpl = pix->bytesperline;
> +	fmt->planesFmt[0].size = pix->sizeimage;
> +

I think we might need to cache the V4L2DeviceFormat in the V4L2Device
somewhere...

But perhaps that's not required by this patch - if something needs to
access this (like the BufferPool creation) it can handle caching the
format object.



> +	return 0;
> +}
> +
> +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	pix->width = fmt->width;
> +	pix->height = fmt->height;
> +	pix->pixelformat = fmt->fourcc;
> +
> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	fmt->width = pix->width;
> +	fmt->height = pix->height;
> +	fmt->fourcc = pix->pixelformat;
> +	fmt->planes = pix->num_planes;
> +
> +	for (unsigned int i = 0; i < fmt->planes; ++i) {
> +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
> +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
> +	}
> +
> +	return 0;
> +}
> +
> +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	pix->width = fmt->width;
> +	pix->height = fmt->height;
> +	pix->pixelformat = fmt->fourcc;
> +	pix->num_planes = fmt->planes;
> +
> +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
> +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
> +	}
> +
> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  } /* namespace libcamera */
>
Jacopo Mondi Jan. 28, 2019, 4:07 p.m. UTC | #2
Hi Kieran,

On Mon, Jan 28, 2019 at 03:42:27PM +0000, Kieran Bingham wrote:
> Hi Jacopo,
>
> I only have variable/layout concerns here - which is certainly not a
> blocker at the moment.
>
> Also - if the outcome of the discussion below changes this patches
> expectations - then we'll need to adapt other patches too - so that
> would be a global patch fix at that point.
>
> Thus I don't think that discussion blocks this patch.
>
>
> On 28/01/2019 15:11, Jacopo Mondi wrote:
> > Add methods to set and get the image format programmed on a V4L2Device
> > for both the single and multi planar use case.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>
>
> > ---
> >  src/libcamera/include/v4l2_device.h |  10 +++
> >  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
> >  2 files changed, 138 insertions(+)
> >
> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> > index c70959e..fe54220 100644
> > --- a/src/libcamera/include/v4l2_device.h
> > +++ b/src/libcamera/include/v4l2_device.h
> > @@ -86,10 +86,20 @@ public:
> >  	const char *deviceName() const { return caps_.card(); }
> >  	const char *busName() const { return caps_.bus_info(); }
> >
> > +	int format(V4L2DeviceFormat *fmt);
> > +	int setFormat(V4L2DeviceFormat *fmt);
> > +
> >  private:
> >  	std::string deviceNode_;
> >  	int fd_;
> >  	V4L2Capability caps_;
> > +	enum v4l2_buf_type bufferType_;
> > +
> > +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> > +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> > +
> > +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> > +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
>
>
> Can I confirm <or prompt discussion> on our class layouts?
>
> Have we defined a layout anywhere?
>
> Here this creates:
>
>
> class ClassName
> {
> public:
> 	ClassName();
> 	~ClassName();
> 	void publicFunctions();
>
> 	int publicData;
> 	bool publicBool;
>
> private:
> 	int privateData;
> 	bool privateBool;
>
> 	int privateFunctions();
> 	int morePrivateFunctions();
> }
>
> Which is:
> 	PublicFunctions
> 	PublicData
> 	PrivateData
> 	PrivateFunctions
>
>
> So the 'data' both public and private is sandwiched in the middle.
> I don't mind this - but in my mind I thought we were doing:
>
>
> class ClassName
> {
> public:
> 	ClassName();
> 	~ClassName();
> 	void publicFunctions();
>
> 	int publicData;
> 	bool publicBool;
>
> private:
> 	int privateFunctions();
> 	int morePrivateFunctions();
>
> 	int privateData;
> 	bool privateBool;
> }
>
> Which is:
> 	PublicFunctions
> 	PublicData
> 	PrivateFunctions
> 	PrivateData
>
> so that the public, and private (or protected) sections follow the same
> sequence?
>
> Any comments from the team here?

Quoting the Google's C++ style guide (which I refer to because we
actually started from there when we defined our coding guidelines):
https://google.github.io/styleguide/cppguide.html#Declaration_Order

... generally prefer the following order: types (including typedef, using,
and nested structs and classes), constants, factory functions, constructors,
assignment operators, destructor, all other methods, data members.

So your
 	PublicFunctions
 	PublicData
 	PrivateFunctions
 	PrivateData

Is accurate.

I fear we would need a library-wide cleanup, but I can start by making
this right at least.

>
> >  };
> >
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > index d6143f2..5c415d0 100644
> > --- a/src/libcamera/v4l2_device.cpp
> > +++ b/src/libcamera/v4l2_device.cpp
> > @@ -227,6 +227,15 @@ int V4L2Device::open()
> >  		return -EINVAL;
> >  	}
> >
> > +	if (caps_.isCapture())
> > +		bufferType_ = caps_.isMultiplanar()
> > +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
> > +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > +	else
> > +		bufferType_ = caps_.isMultiplanar()
> > +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> > +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> > +
> >  	return 0;
> >  }
> >
> > @@ -269,4 +278,123 @@ void V4L2Device::close()
> >   * \return The string containing the device location
> >   */
> >
> > +/**
> > + * \brief Retrieve the image format set on the V4L2 device
> > + * \return 0 for success, a negative error code otherwise
> > + */
> > +int V4L2Device::format(V4L2DeviceFormat *fmt)
> > +{> +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
>
> I think the precedence is that the : would be below the ? here ?
>
> But I'm doubting myself - so hopefully Laurent will chime in with his
> preferences here.
>
>
> If so perhaps we should set the following change in .clang-format:
>
> -BreakBeforeTernaryOperators: false
> +BreakBeforeTernaryOperators: true
>
> which would produce the following:
>
>
> +       return caps_.isMultiplanar() ? getFormatMultiplane(fmt)
> +                                    : getFormatSingleplane(fmt);
>
>
>
> It doesn't however align the ? with the = in the bufferType_ above:
>
>               bufferType_ = caps_.isMultiplanar()
>                           ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
>                           : V4L2_BUF_TYPE_VIDEO_OUTPUT;
>                                     ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
>                                     : V4L2_BUF_TYPE_VIDEO_OUTPUT;
>
> So again - I think that's a non-blocker currently.

What makes the style checker and most of the team happy, makes me
happy, so I'm open to all this solutions.

>
>
> > +				       getFormatSingleplane(fmt);
> > +}
> > +
> > +/**
> > + * \brief Configure an image format on the V4L2 device
> > + * \return 0 for success, a negative error code otherwise
> > + */
> > +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
> > +{
> > +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
> > +				       setFormatSingleplane(fmt);
> > +}
> > +
> > +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	fmt->width = pix->width;
> > +	fmt->height = pix->height;
> > +	fmt->fourcc = pix->pixelformat;
> > +	fmt->planes = 1;
> > +	fmt->planesFmt[0].bpl = pix->bytesperline;
> > +	fmt->planesFmt[0].size = pix->sizeimage;
> > +
>
> I think we might need to cache the V4L2DeviceFormat in the V4L2Device
> somewhere...
>

Yes, indeed. I would have had a V4L2DataFormat member initialized at
device 'open()' time that could be cached, and re-freshed at every
s_fmt..

> But perhaps that's not required by this patch - if something needs to
> access this (like the BufferPool creation) it can handle caching the
> format object.
>

I left it out from this two patches to ease integration, but I agree
this is a possible optimization, and if any class needs that, it shall
be done here.

Thanks
  j
>
>
> > +	return 0;
> > +}
> > +
> > +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	pix->width = fmt->width;
> > +	pix->height = fmt->height;
> > +	pix->pixelformat = fmt->fourcc;
> > +
> > +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	fmt->width = pix->width;
> > +	fmt->height = pix->height;
> > +	fmt->fourcc = pix->pixelformat;
> > +	fmt->planes = pix->num_planes;
> > +
> > +	for (unsigned int i = 0; i < fmt->planes; ++i) {
> > +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
> > +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	pix->width = fmt->width;
> > +	pix->height = fmt->height;
> > +	pix->pixelformat = fmt->fourcc;
> > +	pix->num_planes = fmt->planes;
> > +
> > +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> > +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
> > +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
> > +	}
> > +
> > +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  } /* namespace libcamera */
> >
>
> --
> Regards
> --
> Kieran
Kieran Bingham Jan. 29, 2019, 2:01 p.m. UTC | #3
Hi Jacopo,

On 28/01/2019 16:07, Jacopo Mondi wrote:
> Hi Kieran,
> 
> On Mon, Jan 28, 2019 at 03:42:27PM +0000, Kieran Bingham wrote:
>> Hi Jacopo,
>>
>> I only have variable/layout concerns here - which is certainly not a
>> blocker at the moment.
>>
>> Also - if the outcome of the discussion below changes this patches
>> expectations - then we'll need to adapt other patches too - so that
>> would be a global patch fix at that point.
>>
>> Thus I don't think that discussion blocks this patch.
>>
>>
>> On 28/01/2019 15:11, Jacopo Mondi wrote:
>>> Add methods to set and get the image format programmed on a V4L2Device
>>> for both the single and multi planar use case.
>>>
>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
>>
>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>
>>
>>> ---
>>>  src/libcamera/include/v4l2_device.h |  10 +++
>>>  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
>>>  2 files changed, 138 insertions(+)
>>>
>>> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
>>> index c70959e..fe54220 100644
>>> --- a/src/libcamera/include/v4l2_device.h
>>> +++ b/src/libcamera/include/v4l2_device.h
>>> @@ -86,10 +86,20 @@ public:
>>>  	const char *deviceName() const { return caps_.card(); }
>>>  	const char *busName() const { return caps_.bus_info(); }
>>>
>>> +	int format(V4L2DeviceFormat *fmt);
>>> +	int setFormat(V4L2DeviceFormat *fmt);
>>> +
>>>  private:
>>>  	std::string deviceNode_;
>>>  	int fd_;
>>>  	V4L2Capability caps_;
>>> +	enum v4l2_buf_type bufferType_;
>>> +
>>> +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
>>> +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
>>> +
>>> +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
>>> +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
>>
>>
>> Can I confirm <or prompt discussion> on our class layouts?
>>
>> Have we defined a layout anywhere?
>>
>> Here this creates:
>>
>>
>> class ClassName
>> {
>> public:
>> 	ClassName();
>> 	~ClassName();
>> 	void publicFunctions();
>>
>> 	int publicData;
>> 	bool publicBool;
>>
>> private:
>> 	int privateData;
>> 	bool privateBool;
>>
>> 	int privateFunctions();
>> 	int morePrivateFunctions();
>> }
>>
>> Which is:
>> 	PublicFunctions
>> 	PublicData
>> 	PrivateData
>> 	PrivateFunctions
>>
>>
>> So the 'data' both public and private is sandwiched in the middle.
>> I don't mind this - but in my mind I thought we were doing:
>>
>>
>> class ClassName
>> {
>> public:
>> 	ClassName();
>> 	~ClassName();
>> 	void publicFunctions();
>>
>> 	int publicData;
>> 	bool publicBool;
>>
>> private:
>> 	int privateFunctions();
>> 	int morePrivateFunctions();
>>
>> 	int privateData;
>> 	bool privateBool;
>> }
>>
>> Which is:
>> 	PublicFunctions
>> 	PublicData
>> 	PrivateFunctions
>> 	PrivateData
>>
>> so that the public, and private (or protected) sections follow the same
>> sequence?
>>
>> Any comments from the team here?
> 
> Quoting the Google's C++ style guide (which I refer to because we
> actually started from there when we defined our coding guidelines):
> https://google.github.io/styleguide/cppguide.html#Declaration_Order
> 
> ... generally prefer the following order: types (including typedef, using,
> and nested structs and classes), constants, factory functions, constructors,
> assignment operators, destructor, all other methods, data members.
> 
> So your
>  	PublicFunctions
>  	PublicData
>  	PrivateFunctions
>  	PrivateData
> 
> Is accurate.
> 
> I fear we would need a library-wide cleanup, but I can start by making
> this right at least.
> 

Aha - great - something was right in my head :)

I'd be happy with a push to master with this ordering corrected.

--
Thanks

Kieran


>>
>>>  };
>>>
>>>  } /* namespace libcamera */
>>> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
>>> index d6143f2..5c415d0 100644
>>> --- a/src/libcamera/v4l2_device.cpp
>>> +++ b/src/libcamera/v4l2_device.cpp
>>> @@ -227,6 +227,15 @@ int V4L2Device::open()
>>>  		return -EINVAL;
>>>  	}
>>>
>>> +	if (caps_.isCapture())
>>> +		bufferType_ = caps_.isMultiplanar()
>>> +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
>>> +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
>>> +	else
>>> +		bufferType_ = caps_.isMultiplanar()
>>> +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
>>> +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>> +
>>>  	return 0;
>>>  }
>>>
>>> @@ -269,4 +278,123 @@ void V4L2Device::close()
>>>   * \return The string containing the device location
>>>   */
>>>
>>> +/**
>>> + * \brief Retrieve the image format set on the V4L2 device
>>> + * \return 0 for success, a negative error code otherwise
>>> + */
>>> +int V4L2Device::format(V4L2DeviceFormat *fmt)
>>> +{> +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
>>
>> I think the precedence is that the : would be below the ? here ?
>>
>> But I'm doubting myself - so hopefully Laurent will chime in with his
>> preferences here.
>>
>>
>> If so perhaps we should set the following change in .clang-format:
>>
>> -BreakBeforeTernaryOperators: false
>> +BreakBeforeTernaryOperators: true
>>
>> which would produce the following:
>>
>>
>> +       return caps_.isMultiplanar() ? getFormatMultiplane(fmt)
>> +                                    : getFormatSingleplane(fmt);
>>
>>
>>
>> It doesn't however align the ? with the = in the bufferType_ above:
>>
>>               bufferType_ = caps_.isMultiplanar()
>>                           ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
>>                           : V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>                                     ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
>>                                     : V4L2_BUF_TYPE_VIDEO_OUTPUT;
>>
>> So again - I think that's a non-blocker currently.
> 
> What makes the style checker and most of the team happy, makes me
> happy, so I'm open to all this solutions.

I think the : on the new line makes sense, as it ensures it can't be
confused to be a ; at the end of the line.

The indentation - well I think that's authors choice at this stage.
Either vertically aligned, or whatever makes clang-format happy.



> 
>>
>>
>>> +				       getFormatSingleplane(fmt);
>>> +}
>>> +
>>> +/**
>>> + * \brief Configure an image format on the V4L2 device
>>> + * \return 0 for success, a negative error code otherwise
>>> + */
>>> +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
>>> +{
>>> +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
>>> +				       setFormatSingleplane(fmt);
>>> +}
>>> +
>>> +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
>>> +{
>>> +	struct v4l2_format v4l2Fmt;
>>> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
>>> +	int ret;
>>> +
>>> +	v4l2Fmt.type = bufferType_;
>>> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
>>> +	if (ret) {
>>> +		ret = -errno;
>>> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	fmt->width = pix->width;
>>> +	fmt->height = pix->height;
>>> +	fmt->fourcc = pix->pixelformat;
>>> +	fmt->planes = 1;
>>> +	fmt->planesFmt[0].bpl = pix->bytesperline;
>>> +	fmt->planesFmt[0].size = pix->sizeimage;
>>> +
>>
>> I think we might need to cache the V4L2DeviceFormat in the V4L2Device
>> somewhere...
>>
> 
> Yes, indeed. I would have had a V4L2DataFormat member initialized at
> device 'open()' time that could be cached, and re-freshed at every
> s_fmt..
> 
>> But perhaps that's not required by this patch - if something needs to
>> access this (like the BufferPool creation) it can handle caching the
>> format object.
>>
> 
> I left it out from this two patches to ease integration, but I agree
> this is a possible optimization, and if any class needs that, it shall
> be done here.

I agree - not needed by this patch. Just thinking out loud as ever.

--
Kieran


> 
> Thanks
>   j
>>
>>
>>> +	return 0;
>>> +}
>>> +
>>> +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
>>> +{
>>> +	struct v4l2_format v4l2Fmt;
>>> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
>>> +	int ret;
>>> +
>>> +	v4l2Fmt.type = bufferType_;
>>> +	pix->width = fmt->width;
>>> +	pix->height = fmt->height;
>>> +	pix->pixelformat = fmt->fourcc;
>>> +
>>> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
>>> +	if (ret) {
>>> +		ret = -errno;
>>> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
>>> +{
>>> +	struct v4l2_format v4l2Fmt;
>>> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
>>> +	int ret;
>>> +
>>> +	v4l2Fmt.type = bufferType_;
>>> +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
>>> +	if (ret) {
>>> +		ret = -errno;
>>> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	fmt->width = pix->width;
>>> +	fmt->height = pix->height;
>>> +	fmt->fourcc = pix->pixelformat;
>>> +	fmt->planes = pix->num_planes;
>>> +
>>> +	for (unsigned int i = 0; i < fmt->planes; ++i) {
>>> +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
>>> +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
>>> +{
>>> +	struct v4l2_format v4l2Fmt;
>>> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
>>> +	int ret;
>>> +
>>> +	v4l2Fmt.type = bufferType_;
>>> +	pix->width = fmt->width;
>>> +	pix->height = fmt->height;
>>> +	pix->pixelformat = fmt->fourcc;
>>> +	pix->num_planes = fmt->planes;
>>> +
>>> +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
>>> +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
>>> +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
>>> +	}
>>> +
>>> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
>>> +	if (ret) {
>>> +		ret = -errno;
>>> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>>  } /* namespace libcamera */
>>>
>>
>> --
>> Regards
>> --
>> Kieran
Jacopo Mondi Jan. 29, 2019, 2:54 p.m. UTC | #4
Hi Kieran,

On Tue, Jan 29, 2019 at 02:01:03PM +0000, Kieran Bingham wrote:
> Hi Jacopo,
>
> On 28/01/2019 16:07, Jacopo Mondi wrote:
> > Hi Kieran,
> >
> > On Mon, Jan 28, 2019 at 03:42:27PM +0000, Kieran Bingham wrote:
> >> Hi Jacopo,
> >>
> >> I only have variable/layout concerns here - which is certainly not a
> >> blocker at the moment.
> >>
> >> Also - if the outcome of the discussion below changes this patches
> >> expectations - then we'll need to adapt other patches too - so that
> >> would be a global patch fix at that point.
> >>
> >> Thus I don't think that discussion blocks this patch.
> >>
> >>
> >> On 28/01/2019 15:11, Jacopo Mondi wrote:
> >>> Add methods to set and get the image format programmed on a V4L2Device
> >>> for both the single and multi planar use case.
> >>>
> >>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> >>
> >> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >>
> >>
> >>> ---
> >>>  src/libcamera/include/v4l2_device.h |  10 +++
> >>>  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
> >>>  2 files changed, 138 insertions(+)
> >>>
> >>> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> >>> index c70959e..fe54220 100644
> >>> --- a/src/libcamera/include/v4l2_device.h
> >>> +++ b/src/libcamera/include/v4l2_device.h
> >>> @@ -86,10 +86,20 @@ public:
> >>>  	const char *deviceName() const { return caps_.card(); }
> >>>  	const char *busName() const { return caps_.bus_info(); }
> >>>
> >>> +	int format(V4L2DeviceFormat *fmt);
> >>> +	int setFormat(V4L2DeviceFormat *fmt);
> >>> +
> >>>  private:
> >>>  	std::string deviceNode_;
> >>>  	int fd_;
> >>>  	V4L2Capability caps_;
> >>> +	enum v4l2_buf_type bufferType_;
> >>> +
> >>> +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> >>> +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> >>> +
> >>> +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> >>> +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
> >>
> >>
> >> Can I confirm <or prompt discussion> on our class layouts?
> >>
> >> Have we defined a layout anywhere?
> >>
> >> Here this creates:
> >>
> >>
> >> class ClassName
> >> {
> >> public:
> >> 	ClassName();
> >> 	~ClassName();
> >> 	void publicFunctions();
> >>
> >> 	int publicData;
> >> 	bool publicBool;
> >>
> >> private:
> >> 	int privateData;
> >> 	bool privateBool;
> >>
> >> 	int privateFunctions();
> >> 	int morePrivateFunctions();
> >> }
> >>
> >> Which is:
> >> 	PublicFunctions
> >> 	PublicData
> >> 	PrivateData
> >> 	PrivateFunctions
> >>
> >>
> >> So the 'data' both public and private is sandwiched in the middle.
> >> I don't mind this - but in my mind I thought we were doing:
> >>
> >>
> >> class ClassName
> >> {
> >> public:
> >> 	ClassName();
> >> 	~ClassName();
> >> 	void publicFunctions();
> >>
> >> 	int publicData;
> >> 	bool publicBool;
> >>
> >> private:
> >> 	int privateFunctions();
> >> 	int morePrivateFunctions();
> >>
> >> 	int privateData;
> >> 	bool privateBool;
> >> }
> >>
> >> Which is:
> >> 	PublicFunctions
> >> 	PublicData
> >> 	PrivateFunctions
> >> 	PrivateData
> >>
> >> so that the public, and private (or protected) sections follow the same
> >> sequence?
> >>
> >> Any comments from the team here?
> >
> > Quoting the Google's C++ style guide (which I refer to because we
> > actually started from there when we defined our coding guidelines):
> > https://google.github.io/styleguide/cppguide.html#Declaration_Order
> >
> > ... generally prefer the following order: types (including typedef, using,
> > and nested structs and classes), constants, factory functions, constructors,
> > assignment operators, destructor, all other methods, data members.
> >
> > So your
> >  	PublicFunctions
> >  	PublicData
> >  	PrivateFunctions
> >  	PrivateData
> >
> > Is accurate.
> >
> > I fear we would need a library-wide cleanup, but I can start by making
> > this right at least.
> >
>
> Aha - great - something was right in my head :)
>
> I'd be happy with a push to master with this ordering corrected.
>

Thanks, fixed and pushed!

Thanks
  j
Laurent Pinchart Jan. 30, 2019, 11 a.m. UTC | #5
Hello,

On Mon, Jan 28, 2019 at 05:07:32PM +0100, Jacopo Mondi wrote:
> On Mon, Jan 28, 2019 at 03:42:27PM +0000, Kieran Bingham wrote:
> > Hi Jacopo,
> >
> > I only have variable/layout concerns here - which is certainly not a
> > blocker at the moment.
> >
> > Also - if the outcome of the discussion below changes this patches
> > expectations - then we'll need to adapt other patches too - so that
> > would be a global patch fix at that point.
> >
> > Thus I don't think that discussion blocks this patch.
> >
> > On 28/01/2019 15:11, Jacopo Mondi wrote:
> >> Add methods to set and get the image format programmed on a V4L2Device
> >> for both the single and multi planar use case.
> >>
> >> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> >
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >
> >> ---
> >>  src/libcamera/include/v4l2_device.h |  10 +++
> >>  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
> >>  2 files changed, 138 insertions(+)
> >>
> >> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> >> index c70959e..fe54220 100644
> >> --- a/src/libcamera/include/v4l2_device.h
> >> +++ b/src/libcamera/include/v4l2_device.h
> >> @@ -86,10 +86,20 @@ public:
> >>  	const char *deviceName() const { return caps_.card(); }
> >>  	const char *busName() const { return caps_.bus_info(); }
> >>
> >> +	int format(V4L2DeviceFormat *fmt);
> >> +	int setFormat(V4L2DeviceFormat *fmt);
> >> +
> >>  private:
> >>  	std::string deviceNode_;
> >>  	int fd_;
> >>  	V4L2Capability caps_;
> >> +	enum v4l2_buf_type bufferType_;
> >> +
> >> +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> >> +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> >> +
> >> +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> >> +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
> >
> > Can I confirm <or prompt discussion> on our class layouts?
> >
> > Have we defined a layout anywhere?
> >
> > Here this creates:
> >
> >
> > class ClassName
> > {
> > public:
> > 	ClassName();
> > 	~ClassName();
> > 	void publicFunctions();
> >
> > 	int publicData;
> > 	bool publicBool;
> >
> > private:
> > 	int privateData;
> > 	bool privateBool;
> >
> > 	int privateFunctions();
> > 	int morePrivateFunctions();
> > }
> >
> > Which is:
> > 	PublicFunctions
> > 	PublicData
> > 	PrivateData
> > 	PrivateFunctions
> >
> >
> > So the 'data' both public and private is sandwiched in the middle.
> > I don't mind this - but in my mind I thought we were doing:
> >
> >
> > class ClassName
> > {
> > public:
> > 	ClassName();
> > 	~ClassName();
> > 	void publicFunctions();
> >
> > 	int publicData;
> > 	bool publicBool;
> >
> > private:
> > 	int privateFunctions();
> > 	int morePrivateFunctions();
> >
> > 	int privateData;
> > 	bool privateBool;
> > }
> >
> > Which is:
> > 	PublicFunctions
> > 	PublicData
> > 	PrivateFunctions
> > 	PrivateData
> >
> > so that the public, and private (or protected) sections follow the same
> > sequence?
> >
> > Any comments from the team here?
> 
> Quoting the Google's C++ style guide (which I refer to because we
> actually started from there when we defined our coding guidelines):
> https://google.github.io/styleguide/cppguide.html#Declaration_Order
> 
> ... generally prefer the following order: types (including typedef, using,
> and nested structs and classes), constants, factory functions, constructors,
> assignment operators, destructor, all other methods, data members.
> 
> So your
>  	PublicFunctions
>  	PublicData
>  	PrivateFunctions
>  	PrivateData
> 
> Is accurate.

That looks good to me.

> I fear we would need a library-wide cleanup, but I can start by making
> this right at least.

The cleanup would be quite simple, so let's do it :-)

> >>  };
> >>
> >>  } /* namespace libcamera */
> >> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> >> index d6143f2..5c415d0 100644
> >> --- a/src/libcamera/v4l2_device.cpp
> >> +++ b/src/libcamera/v4l2_device.cpp
> >> @@ -227,6 +227,15 @@ int V4L2Device::open()
> >>  		return -EINVAL;
> >>  	}
> >>
> >> +	if (caps_.isCapture())
> >> +		bufferType_ = caps_.isMultiplanar()
> >> +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
> >> +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
> >> +	else
> >> +		bufferType_ = caps_.isMultiplanar()
> >> +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> >> +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >> +
> >>  	return 0;
> >>  }
> >>
> >> @@ -269,4 +278,123 @@ void V4L2Device::close()
> >>   * \return The string containing the device location
> >>   */
> >>
> >> +/**
> >> + * \brief Retrieve the image format set on the V4L2 device
> >> + * \return 0 for success, a negative error code otherwise
> >> + */
> >> +int V4L2Device::format(V4L2DeviceFormat *fmt)
> >> +{> +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
> >
> > I think the precedence is that the : would be below the ? here ?
> >
> > But I'm doubting myself - so hopefully Laurent will chime in with his
> > preferences here.
> >
> >
> > If so perhaps we should set the following change in .clang-format:
> >
> > -BreakBeforeTernaryOperators: false
> > +BreakBeforeTernaryOperators: true
> >
> > which would produce the following:
> >
> >
> > +       return caps_.isMultiplanar() ? getFormatMultiplane(fmt)
> > +                                    : getFormatSingleplane(fmt);

I also prefer this. In general I find code more readable when the
operator is a the beginning of the line, not the end. Jacopo, could you
please fix this ?

> >
> > It doesn't however align the ? with the = in the bufferType_ above:
> >
> >               bufferType_ = caps_.isMultiplanar()
> >                           ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> >                           : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >                                     ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> >                                     : V4L2_BUF_TYPE_VIDEO_OUTPUT;

It should definitely be aligned with the =.

> > So again - I think that's a non-blocker currently.
> 
> What makes the style checker and most of the team happy, makes me
> happy, so I'm open to all this solutions.
> 
> >> +				       getFormatSingleplane(fmt);
> >> +}
> >> +
> >> +/**
> >> + * \brief Configure an image format on the V4L2 device
> >> + * \return 0 for success, a negative error code otherwise
> >> + */
> >> +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
> >> +{
> >> +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
> >> +				       setFormatSingleplane(fmt);
> >> +}
> >> +
> >> +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	fmt->width = pix->width;
> >> +	fmt->height = pix->height;
> >> +	fmt->fourcc = pix->pixelformat;
> >> +	fmt->planes = 1;
> >> +	fmt->planesFmt[0].bpl = pix->bytesperline;
> >> +	fmt->planesFmt[0].size = pix->sizeimage;
> >> +
> >
> > I think we might need to cache the V4L2DeviceFormat in the V4L2Device
> > somewhere...
> 
> Yes, indeed. I would have had a V4L2DataFormat member initialized at
> device 'open()' time that could be cached, and re-freshed at every
> s_fmt..
> 
> > But perhaps that's not required by this patch - if something needs to
> > access this (like the BufferPool creation) it can handle caching the
> > format object.
> 
> I left it out from this two patches to ease integration, but I agree
> this is a possible optimization, and if any class needs that, it shall
> be done here.

Could the device allowed to change format without userspace intervention ?

> >> +	return 0;
> >> +}
> >> +
> >> +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	pix->width = fmt->width;
> >> +	pix->height = fmt->height;
> >> +	pix->pixelformat = fmt->fourcc;
> >> +
> >> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	fmt->width = pix->width;
> >> +	fmt->height = pix->height;
> >> +	fmt->fourcc = pix->pixelformat;
> >> +	fmt->planes = pix->num_planes;
> >> +
> >> +	for (unsigned int i = 0; i < fmt->planes; ++i) {
> >> +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
> >> +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	pix->width = fmt->width;
> >> +	pix->height = fmt->height;
> >> +	pix->pixelformat = fmt->fourcc;
> >> +	pix->num_planes = fmt->planes;
> >> +
> >> +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> >> +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
> >> +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
> >> +	}
> >> +
> >> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>  } /* namespace libcamera */
Laurent Pinchart Jan. 30, 2019, 11:10 a.m. UTC | #6
Hi Jacopo,

Thank you for the patch.

On Mon, Jan 28, 2019 at 04:11:37PM +0100, Jacopo Mondi wrote:
> Add methods to set and get the image format programmed on a V4L2Device
> for both the single and multi planar use case.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
>  src/libcamera/include/v4l2_device.h |  10 +++
>  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
>  2 files changed, 138 insertions(+)
> 
> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> index c70959e..fe54220 100644
> --- a/src/libcamera/include/v4l2_device.h
> +++ b/src/libcamera/include/v4l2_device.h
> @@ -86,10 +86,20 @@ public:
>  	const char *deviceName() const { return caps_.card(); }
>  	const char *busName() const { return caps_.bus_info(); }
>  
> +	int format(V4L2DeviceFormat *fmt);

As an exception to the general rule, I'd rename format() to getFormat()
to follow the V4L2 naming. I won't push hard for this, but I think this
was the general preference of the team as well.

> +	int setFormat(V4L2DeviceFormat *fmt);

How about spelling format in full (here and below) for the function
parameter ?

> +
>  private:
>  	std::string deviceNode_;
>  	int fd_;
>  	V4L2Capability caps_;
> +	enum v4l2_buf_type bufferType_;
> +
> +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> +
> +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
>  };
>  
>  } /* namespace libcamera */
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> index d6143f2..5c415d0 100644
> --- a/src/libcamera/v4l2_device.cpp
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -227,6 +227,15 @@ int V4L2Device::open()
>  		return -EINVAL;
>  	}
>  
> +	if (caps_.isCapture())
> +		bufferType_ = caps_.isMultiplanar()
> +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
> +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	else
> +		bufferType_ = caps_.isMultiplanar()
> +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> +
>  	return 0;
>  }
>  
> @@ -269,4 +278,123 @@ void V4L2Device::close()
>   * \return The string containing the device location
>   */
>  
> +/**
> + * \brief Retrieve the image format set on the V4L2 device
> + * \return 0 for success, a negative error code otherwise

s/for/on/

> + */
> +int V4L2Device::format(V4L2DeviceFormat *fmt)
> +{
> +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
> +				       getFormatSingleplane(fmt);
> +}
> +
> +/**
> + * \brief Configure an image format on the V4L2 device
> + * \return 0 for success, a negative error code otherwise

s/for/on/

> + */
> +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
> +{
> +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
> +				       setFormatSingleplane(fmt);
> +}
> +
> +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;

v4l2Format ? Or maybe just fmt if you rename the function parameter to
format ?

You need to initialize v4l2Fmt to 0, that's a requirement of the V4L2
API. = {}; will suffice.

> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);

Shouldn't this be VIDIOC_G_FMT ? This outlines a missing test case :-)

> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	fmt->width = pix->width;
> +	fmt->height = pix->height;
> +	fmt->fourcc = pix->pixelformat;
> +	fmt->planes = 1;
> +	fmt->planesFmt[0].bpl = pix->bytesperline;
> +	fmt->planesFmt[0].size = pix->sizeimage;
> +
> +	return 0;
> +}
> +
> +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	pix->width = fmt->width;
> +	pix->height = fmt->height;
> +	pix->pixelformat = fmt->fourcc;

How about bytesperline and sizeimage ?

> +
> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> +		return ret;
> +	}
> +

Shouldn't fmt be updated ?

> +	return 0;
> +}
> +
> +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)

The above comments hold for the mplane functions too.

> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	fmt->width = pix->width;
> +	fmt->height = pix->height;
> +	fmt->fourcc = pix->pixelformat;
> +	fmt->planes = pix->num_planes;
> +
> +	for (unsigned int i = 0; i < fmt->planes; ++i) {
> +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
> +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
> +	}
> +
> +	return 0;
> +}
> +
> +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
> +{
> +	struct v4l2_format v4l2Fmt;
> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> +	int ret;
> +
> +	v4l2Fmt.type = bufferType_;
> +	pix->width = fmt->width;
> +	pix->height = fmt->height;
> +	pix->pixelformat = fmt->fourcc;
> +	pix->num_planes = fmt->planes;
> +
> +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
> +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
> +	}
> +
> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> +	if (ret) {
> +		ret = -errno;
> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  } /* namespace libcamera */
Jacopo Mondi Jan. 30, 2019, 2:37 p.m. UTC | #7
Hi Laurent,

On Wed, Jan 30, 2019 at 01:10:00PM +0200, Laurent Pinchart wrote:
> Hi Jacopo,
>
> Thank you for the patch.
>
> On Mon, Jan 28, 2019 at 04:11:37PM +0100, Jacopo Mondi wrote:
> > Add methods to set and get the image format programmed on a V4L2Device
> > for both the single and multi planar use case.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > ---
> >  src/libcamera/include/v4l2_device.h |  10 +++
> >  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
> >  2 files changed, 138 insertions(+)
> >
> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> > index c70959e..fe54220 100644
> > --- a/src/libcamera/include/v4l2_device.h
> > +++ b/src/libcamera/include/v4l2_device.h
> > @@ -86,10 +86,20 @@ public:
> >  	const char *deviceName() const { return caps_.card(); }
> >  	const char *busName() const { return caps_.bus_info(); }
> >
> > +	int format(V4L2DeviceFormat *fmt);
>
> As an exception to the general rule, I'd rename format() to getFormat()
> to follow the V4L2 naming. I won't push hard for this, but I think this
> was the general preference of the team as well.
>

iirc we discussed that and went for this solution. I can re-change it
in case..

> > +	int setFormat(V4L2DeviceFormat *fmt);
>
> How about spelling format in full (here and below) for the function
> parameter ?
>

Ok

> > +
> >  private:
> >  	std::string deviceNode_;
> >  	int fd_;
> >  	V4L2Capability caps_;
> > +	enum v4l2_buf_type bufferType_;
> > +
> > +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> > +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> > +
> > +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> > +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
> >  };
> >
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > index d6143f2..5c415d0 100644
> > --- a/src/libcamera/v4l2_device.cpp
> > +++ b/src/libcamera/v4l2_device.cpp
> > @@ -227,6 +227,15 @@ int V4L2Device::open()
> >  		return -EINVAL;
> >  	}
> >
> > +	if (caps_.isCapture())
> > +		bufferType_ = caps_.isMultiplanar()
> > +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
> > +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
> > +	else
> > +		bufferType_ = caps_.isMultiplanar()
> > +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> > +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> > +
> >  	return 0;
> >  }
> >
> > @@ -269,4 +278,123 @@ void V4L2Device::close()
> >   * \return The string containing the device location
> >   */
> >
> > +/**
> > + * \brief Retrieve the image format set on the V4L2 device
> > + * \return 0 for success, a negative error code otherwise
>
> s/for/on/
>
> > + */
> > +int V4L2Device::format(V4L2DeviceFormat *fmt)
> > +{
> > +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
> > +				       getFormatSingleplane(fmt);
> > +}
> > +
> > +/**
> > + * \brief Configure an image format on the V4L2 device
> > + * \return 0 for success, a negative error code otherwise
>
> s/for/on/
>
> > + */
> > +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
> > +{
> > +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
> > +				       setFormatSingleplane(fmt);
> > +}
> > +
> > +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
>
> v4l2Format ? Or maybe just fmt if you rename the function parameter to
> format ?
>

Ok

> You need to initialize v4l2Fmt to 0, that's a requirement of the V4L2
> API. = {}; will suffice.
>

correct.

> > +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
>
> Shouldn't this be VIDIOC_G_FMT ? This outlines a missing test case :-)
>

My bad, I'm sorry.
For a test case, how should we make sure we test all possible use
cases (planar/multiplanar, capture/output). The device that runs the
test might not have all of these... Could VIMC or vivid help us here?

> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	fmt->width = pix->width;
> > +	fmt->height = pix->height;
> > +	fmt->fourcc = pix->pixelformat;
> > +	fmt->planes = 1;
> > +	fmt->planesFmt[0].bpl = pix->bytesperline;
> > +	fmt->planesFmt[0].size = pix->sizeimage;
> > +
> > +	return 0;
> > +}
> > +
> > +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	pix->width = fmt->width;
> > +	pix->height = fmt->height;
> > +	pix->pixelformat = fmt->fourcc;
>
> How about bytesperline and sizeimage ?
>

The description here
https://www.kernel.org/doc/html/v4.13/media/uapi/v4l/pixfmt-002.html#c.v4l2_pix_format
quite confused me.

Particularly: "Size in bytes of the buffer to hold a complete image,
set by the driver."

> > +
> > +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
>
> Shouldn't fmt be updated ?
>

Good question, what semantic would we want to assign to this
functions? A setFormat returns an updated format? I here thought to
clearly separate this: to know what is actually set, you have to call
g_fmt again. Honestly, now that I re-think this, I am not convinced
anymore this is the right way, as the V4L2 S_FMT actually returns an
updated format, so it might be worth updating it here before return.

> > +	return 0;
> > +}
> > +
> > +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
>
> The above comments hold for the mplane functions too.
>
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);

At least this one is right...

Thanks
  j

> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	fmt->width = pix->width;
> > +	fmt->height = pix->height;
> > +	fmt->fourcc = pix->pixelformat;
> > +	fmt->planes = pix->num_planes;
> > +
> > +	for (unsigned int i = 0; i < fmt->planes; ++i) {
> > +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
> > +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
> > +{
> > +	struct v4l2_format v4l2Fmt;
> > +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> > +	int ret;
> > +
> > +	v4l2Fmt.type = bufferType_;
> > +	pix->width = fmt->width;
> > +	pix->height = fmt->height;
> > +	pix->pixelformat = fmt->fourcc;
> > +	pix->num_planes = fmt->planes;
> > +
> > +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> > +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
> > +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
> > +	}
> > +
> > +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> > +	if (ret) {
> > +		ret = -errno;
> > +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  } /* namespace libcamera */
>
> --
> Regards,
>
> Laurent Pinchart
Laurent Pinchart Jan. 30, 2019, 10:31 p.m. UTC | #8
Hi Jacopo,

On Wed, Jan 30, 2019 at 03:37:13PM +0100, Jacopo Mondi wrote:
> On Wed, Jan 30, 2019 at 01:10:00PM +0200, Laurent Pinchart wrote:
> > On Mon, Jan 28, 2019 at 04:11:37PM +0100, Jacopo Mondi wrote:
> >> Add methods to set and get the image format programmed on a V4L2Device
> >> for both the single and multi planar use case.
> >>
> >> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> >> ---
> >>  src/libcamera/include/v4l2_device.h |  10 +++
> >>  src/libcamera/v4l2_device.cpp       | 128 ++++++++++++++++++++++++++++
> >>  2 files changed, 138 insertions(+)
> >>
> >> diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> >> index c70959e..fe54220 100644
> >> --- a/src/libcamera/include/v4l2_device.h
> >> +++ b/src/libcamera/include/v4l2_device.h
> >> @@ -86,10 +86,20 @@ public:
> >>  	const char *deviceName() const { return caps_.card(); }
> >>  	const char *busName() const { return caps_.bus_info(); }
> >>
> >> +	int format(V4L2DeviceFormat *fmt);
> >
> > As an exception to the general rule, I'd rename format() to getFormat()
> > to follow the V4L2 naming. I won't push hard for this, but I think this
> > was the general preference of the team as well.
> 
> iirc we discussed that and went for this solution. I can re-change it
> in case..

I think I replied to a previous e-mail explaining that I thought an
exception would make sense here, and I recall (possibly incorrectly)
that you were in favour of getFormat(). As I said, I won't push, but if
we all agree getFormat() makes sense, they I think we should go for it.

> >> +	int setFormat(V4L2DeviceFormat *fmt);
> >
> > How about spelling format in full (here and below) for the function
> > parameter ?
> 
> Ok
> 
> >> +
> >>  private:
> >>  	std::string deviceNode_;
> >>  	int fd_;
> >>  	V4L2Capability caps_;
> >> +	enum v4l2_buf_type bufferType_;
> >> +
> >> +	int getFormatSingleplane(V4L2DeviceFormat *fmt);
> >> +	int setFormatSingleplane(V4L2DeviceFormat *fmt);
> >> +
> >> +	int getFormatMultiplane(V4L2DeviceFormat *fmt);
> >> +	int setFormatMultiplane(V4L2DeviceFormat *fmt);
> >>  };
> >>
> >>  } /* namespace libcamera */
> >> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> >> index d6143f2..5c415d0 100644
> >> --- a/src/libcamera/v4l2_device.cpp
> >> +++ b/src/libcamera/v4l2_device.cpp
> >> @@ -227,6 +227,15 @@ int V4L2Device::open()
> >>  		return -EINVAL;
> >>  	}
> >>
> >> +	if (caps_.isCapture())
> >> +		bufferType_ = caps_.isMultiplanar()
> >> +			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
> >> +			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
> >> +	else
> >> +		bufferType_ = caps_.isMultiplanar()
> >> +			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
> >> +			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> >> +
> >>  	return 0;
> >>  }
> >>
> >> @@ -269,4 +278,123 @@ void V4L2Device::close()
> >>   * \return The string containing the device location
> >>   */
> >>
> >> +/**
> >> + * \brief Retrieve the image format set on the V4L2 device
> >> + * \return 0 for success, a negative error code otherwise
> >
> > s/for/on/
> >
> >> + */
> >> +int V4L2Device::format(V4L2DeviceFormat *fmt)
> >> +{
> >> +	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
> >> +				       getFormatSingleplane(fmt);
> >> +}
> >> +
> >> +/**
> >> + * \brief Configure an image format on the V4L2 device
> >> + * \return 0 for success, a negative error code otherwise
> >
> > s/for/on/
> >
> >> + */
> >> +int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
> >> +{
> >> +	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
> >> +				       setFormatSingleplane(fmt);
> >> +}
> >> +
> >> +int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >
> > v4l2Format ? Or maybe just fmt if you rename the function parameter to
> > format ?
> 
> Ok
> 
> > You need to initialize v4l2Fmt to 0, that's a requirement of the V4L2
> > API. = {}; will suffice.
> >
> 
> correct.
> 
> >> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> >
> > Shouldn't this be VIDIOC_G_FMT ? This outlines a missing test case :-)
> >
> 
> My bad, I'm sorry.
> For a test case, how should we make sure we test all possible use
> cases (planar/multiplanar, capture/output). The device that runs the
> test might not have all of these... Could VIMC or vivid help us here?

I think they could help. I haven't checked which of the
single/multi-planar APIs those drivers use, and whether they could help
testing both, but we could at least test one of the two.

> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	fmt->width = pix->width;
> >> +	fmt->height = pix->height;
> >> +	fmt->fourcc = pix->pixelformat;
> >> +	fmt->planes = 1;
> >> +	fmt->planesFmt[0].bpl = pix->bytesperline;
> >> +	fmt->planesFmt[0].size = pix->sizeimage;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	pix->width = fmt->width;
> >> +	pix->height = fmt->height;
> >> +	pix->pixelformat = fmt->fourcc;
> >
> > How about bytesperline and sizeimage ?
> 
> The description here
> https://www.kernel.org/doc/html/v4.13/media/uapi/v4l/pixfmt-002.html#c.v4l2_pix_format
> quite confused me.
> 
> Particularly: "Size in bytes of the buffer to hold a complete image,
> set by the driver."

You're right about sizeimage. For bytesperline, though, you should pass
it to the ioctl.

> >> +
> >> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >
> > Shouldn't fmt be updated ?
> 
> Good question, what semantic would we want to assign to this
> functions? A setFormat returns an updated format? I here thought to
> clearly separate this: to know what is actually set, you have to call
> g_fmt again. Honestly, now that I re-think this, I am not convinced
> anymore this is the right way, as the V4L2 S_FMT actually returns an
> updated format, so it might be worth updating it here before return.

I think mimicking the VIDIOC_S_FMT semantics would make sense, as it
would save a VIDIOC_G_FMT call for users of this class.

> >> +	return 0;
> >> +}
> >> +
> >> +int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
> >
> > The above comments hold for the mplane functions too.
> >
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
> 
> At least this one is right...

Sometimes copy & paste works ;-)

> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to get format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	fmt->width = pix->width;
> >> +	fmt->height = pix->height;
> >> +	fmt->fourcc = pix->pixelformat;
> >> +	fmt->planes = pix->num_planes;
> >> +
> >> +	for (unsigned int i = 0; i < fmt->planes; ++i) {
> >> +		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
> >> +		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
> >> +{
> >> +	struct v4l2_format v4l2Fmt;
> >> +	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
> >> +	int ret;
> >> +
> >> +	v4l2Fmt.type = bufferType_;
> >> +	pix->width = fmt->width;
> >> +	pix->height = fmt->height;
> >> +	pix->pixelformat = fmt->fourcc;
> >> +	pix->num_planes = fmt->planes;
> >> +
> >> +	for (unsigned int i = 0; i < pix->num_planes; ++i) {
> >> +		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
> >> +		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
> >> +	}
> >> +
> >> +	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
> >> +	if (ret) {
> >> +		ret = -errno;
> >> +		LOG(Error) << "Unable to set format: " << strerror(-ret);
> >> +		return ret;
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >>  } /* namespace libcamera */

Patch

diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
index c70959e..fe54220 100644
--- a/src/libcamera/include/v4l2_device.h
+++ b/src/libcamera/include/v4l2_device.h
@@ -86,10 +86,20 @@  public:
 	const char *deviceName() const { return caps_.card(); }
 	const char *busName() const { return caps_.bus_info(); }
 
+	int format(V4L2DeviceFormat *fmt);
+	int setFormat(V4L2DeviceFormat *fmt);
+
 private:
 	std::string deviceNode_;
 	int fd_;
 	V4L2Capability caps_;
+	enum v4l2_buf_type bufferType_;
+
+	int getFormatSingleplane(V4L2DeviceFormat *fmt);
+	int setFormatSingleplane(V4L2DeviceFormat *fmt);
+
+	int getFormatMultiplane(V4L2DeviceFormat *fmt);
+	int setFormatMultiplane(V4L2DeviceFormat *fmt);
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index d6143f2..5c415d0 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -227,6 +227,15 @@  int V4L2Device::open()
 		return -EINVAL;
 	}
 
+	if (caps_.isCapture())
+		bufferType_ = caps_.isMultiplanar()
+			    ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
+			    : V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	else
+		bufferType_ = caps_.isMultiplanar()
+			    ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
+			    : V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
 	return 0;
 }
 
@@ -269,4 +278,123 @@  void V4L2Device::close()
  * \return The string containing the device location
  */
 
+/**
+ * \brief Retrieve the image format set on the V4L2 device
+ * \return 0 for success, a negative error code otherwise
+ */
+int V4L2Device::format(V4L2DeviceFormat *fmt)
+{
+	return caps_.isMultiplanar() ? getFormatMultiplane(fmt) :
+				       getFormatSingleplane(fmt);
+}
+
+/**
+ * \brief Configure an image format on the V4L2 device
+ * \return 0 for success, a negative error code otherwise
+ */
+int V4L2Device::setFormat(V4L2DeviceFormat *fmt)
+{
+	return caps_.isMultiplanar() ? setFormatMultiplane(fmt) :
+				       setFormatSingleplane(fmt);
+}
+
+int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *fmt)
+{
+	struct v4l2_format v4l2Fmt;
+	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
+	int ret;
+
+	v4l2Fmt.type = bufferType_;
+	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
+	if (ret) {
+		ret = -errno;
+		LOG(Error) << "Unable to get format: " << strerror(-ret);
+		return ret;
+	}
+
+	fmt->width = pix->width;
+	fmt->height = pix->height;
+	fmt->fourcc = pix->pixelformat;
+	fmt->planes = 1;
+	fmt->planesFmt[0].bpl = pix->bytesperline;
+	fmt->planesFmt[0].size = pix->sizeimage;
+
+	return 0;
+}
+
+int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *fmt)
+{
+	struct v4l2_format v4l2Fmt;
+	struct v4l2_pix_format *pix = &v4l2Fmt.fmt.pix;
+	int ret;
+
+	v4l2Fmt.type = bufferType_;
+	pix->width = fmt->width;
+	pix->height = fmt->height;
+	pix->pixelformat = fmt->fourcc;
+
+	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
+	if (ret) {
+		ret = -errno;
+		LOG(Error) << "Unable to set format: " << strerror(-ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *fmt)
+{
+	struct v4l2_format v4l2Fmt;
+	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
+	int ret;
+
+	v4l2Fmt.type = bufferType_;
+	ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Fmt);
+	if (ret) {
+		ret = -errno;
+		LOG(Error) << "Unable to get format: " << strerror(-ret);
+		return ret;
+	}
+
+	fmt->width = pix->width;
+	fmt->height = pix->height;
+	fmt->fourcc = pix->pixelformat;
+	fmt->planes = pix->num_planes;
+
+	for (unsigned int i = 0; i < fmt->planes; ++i) {
+		fmt->planesFmt[i].bpl = pix->plane_fmt[i].bytesperline;
+		fmt->planesFmt[i].size = pix->plane_fmt[i].sizeimage;
+	}
+
+	return 0;
+}
+
+int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *fmt)
+{
+	struct v4l2_format v4l2Fmt;
+	struct v4l2_pix_format_mplane *pix = &v4l2Fmt.fmt.pix_mp;
+	int ret;
+
+	v4l2Fmt.type = bufferType_;
+	pix->width = fmt->width;
+	pix->height = fmt->height;
+	pix->pixelformat = fmt->fourcc;
+	pix->num_planes = fmt->planes;
+
+	for (unsigned int i = 0; i < pix->num_planes; ++i) {
+		pix->plane_fmt[i].bytesperline = fmt->planesFmt[i].bpl;
+		pix->plane_fmt[i].sizeimage = fmt->planesFmt[i].size;
+	}
+
+	ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Fmt);
+	if (ret) {
+		ret = -errno;
+		LOG(Error) << "Unable to set format: " << strerror(-ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 } /* namespace libcamera */