[libcamera-devel,v2,1/2] libcamera: camera_sensor: Fix frame lengths calculated by sensorInfo()
diff mbox series

Message ID 20210406104050.23814-2-david.plowman@raspberrypi.com
State Accepted
Headers show
Series
  • Fix CameraSensor::sensorInfo by updating VBLANK ControlInfo
Related show

Commit Message

David Plowman April 6, 2021, 10:40 a.m. UTC
The minimum and maximum vblanking can change when a new format is
applied to the sensor subdevice, so be sure to retrieve up-to-date
values.

The V4L2Device acquires the new updateControlInfos() method to perform
this function, and which the CameraSensor calls automatically if its
setFormat method is used to update the sensor.

However, not all pipeline handlers invoke the setFormat method
directly, so the new method must be made publicly available for
pipeline handlers to call if they need to.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 include/libcamera/internal/camera_sensor.h |  2 ++
 include/libcamera/internal/v4l2_device.h   |  2 ++
 src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
 src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++
 4 files changed, 60 insertions(+), 1 deletion(-)

Comments

Jacopo Mondi April 6, 2021, 12:10 p.m. UTC | #1
Hi David,

On Tue, Apr 06, 2021 at 11:40:49AM +0100, David Plowman wrote:
> The minimum and maximum vblanking can change when a new format is
> applied to the sensor subdevice, so be sure to retrieve up-to-date
> values.
>
> The V4L2Device acquires the new updateControlInfos() method to perform
> this function, and which the CameraSensor calls automatically if its
> setFormat method is used to update the sensor.
>
> However, not all pipeline handlers invoke the setFormat method
> directly, so the new method must be made publicly available for
> pipeline handlers to call if they need to.
>
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  include/libcamera/internal/camera_sensor.h |  2 ++
>  include/libcamera/internal/v4l2_device.h   |  2 ++
>  src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
>  src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++
>  4 files changed, 60 insertions(+), 1 deletion(-)
>
> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> index 3e98f71b..c94744f0 100644
> --- a/include/libcamera/internal/camera_sensor.h
> +++ b/include/libcamera/internal/camera_sensor.h
> @@ -68,6 +68,8 @@ public:
>  	const ControlList &properties() const { return properties_; }
>  	int sensorInfo(CameraSensorInfo *info) const;
>
> +	void updateControlInfos();
> +
>  protected:
>  	std::string logPrefix() const override;
>
> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
> index d006bf68..274cbe65 100644
> --- a/include/libcamera/internal/v4l2_device.h
> +++ b/include/libcamera/internal/v4l2_device.h
> @@ -41,6 +41,8 @@ public:
>  	int setFrameStartEnabled(bool enable);
>  	Signal<uint32_t> frameStart;
>
> +	void updateControlInfos();
> +
>  protected:
>  	V4L2Device(const std::string &deviceNode);
>  	~V4L2Device();
> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> index f7ed91d9..5dbe47a7 100644
> --- a/src/libcamera/camera_sensor.cpp
> +++ b/src/libcamera/camera_sensor.cpp
> @@ -656,15 +656,30 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
>  /**
>   * \brief Set the sensor output format
>   * \param[in] format The desired sensor output format
> + *
> + * The sensor output format is set. The ranges of any controls associated
> + * with the sensor are also updated.
> + *
>   * \return 0 on success or a negative error code otherwise
>   */
>  int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
>  {
> -	return subdev_->setFormat(pad_, format);
> +	int ret = subdev_->setFormat(pad_, format);
> +
> +	if (ret == 0)
> +		updateControlInfos();
> +
> +	return ret;
>  }
>
>  /**
>   * \brief Retrieve the supported V4L2 controls and their information
> + *
> + * Pipeline handlers that do not change the sensor format using the
> + * CameraSensor::setFormat method may need to call
> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> + * ranges are up to date.
> + *
>   * \return A map of the V4L2 controls supported by the sensor
>   */
>  const ControlInfoMap &CameraSensor::controls() const
> @@ -750,6 +765,11 @@ int CameraSensor::setControls(ControlList *ctrls)
>   * Sensor information is only available for raw sensors. When called for a YUV
>   * sensor, this function returns -EINVAL.
>   *
> + * Pipeline handlers that do not change the sensor format using the
> + * CameraSensor::setFormat method may need to call
> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> + * ranges are up to date.
> + *
>   * \return 0 on success, a negative error code otherwise
>   */
>  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> @@ -821,6 +841,15 @@ int CameraSensor::sensorInfo(CameraSensorInfo *info) const
>  	return 0;
>  }
>
> +/**
> + * \fn void CameraSensor::updateControlInfos()
> + * \brief Update the sensor's ControlInfos in case they have changed
> + */
> +void CameraSensor::updateControlInfos()
> +{
> +	subdev_->updateControlInfos();
> +}
> +
>  std::string CameraSensor::logPrefix() const
>  {
>  	return "'" + entity_->name() + "'";
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> index decd19ef..9678962c 100644
> --- a/src/libcamera/v4l2_device.cpp
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -514,6 +514,32 @@ void V4L2Device::listControls()
>  	controls_ = std::move(ctrls);
>  }
>
> +/*

This is now a public method and needs /**
Doesn't doxygen give you a warning here ?

The patch looks good otherwise!
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Thanks
   j

> + * \brief Update the control information that was previously stored by
> + * listControls(). Some parts of this information, such as min and max
> + * values of some controls, are liable to change when a new format is set.
> + */
> +void V4L2Device::updateControlInfos()
> +{
> +	for (auto &controlId : controlIds_) {
> +		unsigned int id = controlId->id();
> +
> +		struct v4l2_query_ext_ctrl ctrl = {};
> +		ctrl.id = id;
> +
> +		if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
> +			LOG(V4L2, Debug)
> +				<< "Could not refresh control "
> +				<< utils::hex(ctrl.id);
> +			continue;
> +		}
> +
> +		/* Assume these are present - listControls() made them. */
> +		controlInfo_[id] = ctrl;
> +		controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);
> +	}
> +}
> +
>  /*
>   * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
>   * values in \a v4l2Ctrls
> --
> 2.20.1
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
David Plowman April 7, 2021, 9:33 a.m. UTC | #2
Hi Jacopo

On Tue, 6 Apr 2021 at 13:09, Jacopo Mondi <jacopo@jmondi.org> wrote:
>
> Hi David,
>
> On Tue, Apr 06, 2021 at 11:40:49AM +0100, David Plowman wrote:
> > The minimum and maximum vblanking can change when a new format is
> > applied to the sensor subdevice, so be sure to retrieve up-to-date
> > values.
> >
> > The V4L2Device acquires the new updateControlInfos() method to perform
> > this function, and which the CameraSensor calls automatically if its
> > setFormat method is used to update the sensor.
> >
> > However, not all pipeline handlers invoke the setFormat method
> > directly, so the new method must be made publicly available for
> > pipeline handlers to call if they need to.
> >
> > Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > ---
> >  include/libcamera/internal/camera_sensor.h |  2 ++
> >  include/libcamera/internal/v4l2_device.h   |  2 ++
> >  src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
> >  src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++
> >  4 files changed, 60 insertions(+), 1 deletion(-)
> >
> > diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> > index 3e98f71b..c94744f0 100644
> > --- a/include/libcamera/internal/camera_sensor.h
> > +++ b/include/libcamera/internal/camera_sensor.h
> > @@ -68,6 +68,8 @@ public:
> >       const ControlList &properties() const { return properties_; }
> >       int sensorInfo(CameraSensorInfo *info) const;
> >
> > +     void updateControlInfos();
> > +
> >  protected:
> >       std::string logPrefix() const override;
> >
> > diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
> > index d006bf68..274cbe65 100644
> > --- a/include/libcamera/internal/v4l2_device.h
> > +++ b/include/libcamera/internal/v4l2_device.h
> > @@ -41,6 +41,8 @@ public:
> >       int setFrameStartEnabled(bool enable);
> >       Signal<uint32_t> frameStart;
> >
> > +     void updateControlInfos();
> > +
> >  protected:
> >       V4L2Device(const std::string &deviceNode);
> >       ~V4L2Device();
> > diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> > index f7ed91d9..5dbe47a7 100644
> > --- a/src/libcamera/camera_sensor.cpp
> > +++ b/src/libcamera/camera_sensor.cpp
> > @@ -656,15 +656,30 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
> >  /**
> >   * \brief Set the sensor output format
> >   * \param[in] format The desired sensor output format
> > + *
> > + * The sensor output format is set. The ranges of any controls associated
> > + * with the sensor are also updated.
> > + *
> >   * \return 0 on success or a negative error code otherwise
> >   */
> >  int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
> >  {
> > -     return subdev_->setFormat(pad_, format);
> > +     int ret = subdev_->setFormat(pad_, format);
> > +
> > +     if (ret == 0)
> > +             updateControlInfos();
> > +
> > +     return ret;
> >  }
> >
> >  /**
> >   * \brief Retrieve the supported V4L2 controls and their information
> > + *
> > + * Pipeline handlers that do not change the sensor format using the
> > + * CameraSensor::setFormat method may need to call
> > + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> > + * ranges are up to date.
> > + *
> >   * \return A map of the V4L2 controls supported by the sensor
> >   */
> >  const ControlInfoMap &CameraSensor::controls() const
> > @@ -750,6 +765,11 @@ int CameraSensor::setControls(ControlList *ctrls)
> >   * Sensor information is only available for raw sensors. When called for a YUV
> >   * sensor, this function returns -EINVAL.
> >   *
> > + * Pipeline handlers that do not change the sensor format using the
> > + * CameraSensor::setFormat method may need to call
> > + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> > + * ranges are up to date.
> > + *
> >   * \return 0 on success, a negative error code otherwise
> >   */
> >  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> > @@ -821,6 +841,15 @@ int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> >       return 0;
> >  }
> >
> > +/**
> > + * \fn void CameraSensor::updateControlInfos()
> > + * \brief Update the sensor's ControlInfos in case they have changed
> > + */
> > +void CameraSensor::updateControlInfos()
> > +{
> > +     subdev_->updateControlInfos();
> > +}
> > +
> >  std::string CameraSensor::logPrefix() const
> >  {
> >       return "'" + entity_->name() + "'";
> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > index decd19ef..9678962c 100644
> > --- a/src/libcamera/v4l2_device.cpp
> > +++ b/src/libcamera/v4l2_device.cpp
> > @@ -514,6 +514,32 @@ void V4L2Device::listControls()
> >       controls_ = std::move(ctrls);
> >  }
> >
> > +/*
>
> This is now a public method and needs /**
> Doesn't doxygen give you a warning here ?

Ah yes, the documentation seems to have stopped building some time
ago... there appear to be a couple more dependencies I have to install
for it to happen now. No problem!

Thanks
David

>
> The patch looks good otherwise!
> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
>
> Thanks
>    j
>
> > + * \brief Update the control information that was previously stored by
> > + * listControls(). Some parts of this information, such as min and max
> > + * values of some controls, are liable to change when a new format is set.
> > + */
> > +void V4L2Device::updateControlInfos()
> > +{
> > +     for (auto &controlId : controlIds_) {
> > +             unsigned int id = controlId->id();
> > +
> > +             struct v4l2_query_ext_ctrl ctrl = {};
> > +             ctrl.id = id;
> > +
> > +             if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
> > +                     LOG(V4L2, Debug)
> > +                             << "Could not refresh control "
> > +                             << utils::hex(ctrl.id);
> > +                     continue;
> > +             }
> > +
> > +             /* Assume these are present - listControls() made them. */
> > +             controlInfo_[id] = ctrl;
> > +             controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);
> > +     }
> > +}
> > +
> >  /*
> >   * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
> >   * values in \a v4l2Ctrls
> > --
> > 2.20.1
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
Kieran Bingham April 13, 2021, 9:34 p.m. UTC | #3
Hi David,

On 07/04/2021 10:33, David Plowman wrote:
> Hi Jacopo
> 
> On Tue, 6 Apr 2021 at 13:09, Jacopo Mondi <jacopo@jmondi.org> wrote:
>>
>> Hi David,
>>
>> On Tue, Apr 06, 2021 at 11:40:49AM +0100, David Plowman wrote:
>>> The minimum and maximum vblanking can change when a new format is
>>> applied to the sensor subdevice, so be sure to retrieve up-to-date
>>> values.
>>>
>>> The V4L2Device acquires the new updateControlInfos() method to perform
>>> this function, and which the CameraSensor calls automatically if its
>>> setFormat method is used to update the sensor.
>>>
>>> However, not all pipeline handlers invoke the setFormat method
>>> directly, so the new method must be made publicly available for
>>> pipeline handlers to call if they need to.
>>>
>>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
>>> ---
>>>  include/libcamera/internal/camera_sensor.h |  2 ++
>>>  include/libcamera/internal/v4l2_device.h   |  2 ++
>>>  src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
>>>  src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++
>>>  4 files changed, 60 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
>>> index 3e98f71b..c94744f0 100644
>>> --- a/include/libcamera/internal/camera_sensor.h
>>> +++ b/include/libcamera/internal/camera_sensor.h
>>> @@ -68,6 +68,8 @@ public:
>>>       const ControlList &properties() const { return properties_; }
>>>       int sensorInfo(CameraSensorInfo *info) const;
>>>
>>> +     void updateControlInfos();
>>> +
>>>  protected:
>>>       std::string logPrefix() const override;
>>>
>>> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
>>> index d006bf68..274cbe65 100644
>>> --- a/include/libcamera/internal/v4l2_device.h
>>> +++ b/include/libcamera/internal/v4l2_device.h
>>> @@ -41,6 +41,8 @@ public:
>>>       int setFrameStartEnabled(bool enable);
>>>       Signal<uint32_t> frameStart;
>>>
>>> +     void updateControlInfos();
>>> +
>>>  protected:
>>>       V4L2Device(const std::string &deviceNode);
>>>       ~V4L2Device();
>>> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
>>> index f7ed91d9..5dbe47a7 100644
>>> --- a/src/libcamera/camera_sensor.cpp
>>> +++ b/src/libcamera/camera_sensor.cpp
>>> @@ -656,15 +656,30 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
>>>  /**
>>>   * \brief Set the sensor output format
>>>   * \param[in] format The desired sensor output format
>>> + *
>>> + * The sensor output format is set. The ranges of any controls associated
>>> + * with the sensor are also updated.
>>> + *
>>>   * \return 0 on success or a negative error code otherwise
>>>   */
>>>  int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
>>>  {
>>> -     return subdev_->setFormat(pad_, format);
>>> +     int ret = subdev_->setFormat(pad_, format);
>>> +
>>> +     if (ret == 0)
>>> +             updateControlInfos();
>>> +
>>> +     return ret;
>>>  }
>>>
>>>  /**
>>>   * \brief Retrieve the supported V4L2 controls and their information
>>> + *
>>> + * Pipeline handlers that do not change the sensor format using the
>>> + * CameraSensor::setFormat method may need to call
>>> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
>>> + * ranges are up to date.
>>> + *
>>>   * \return A map of the V4L2 controls supported by the sensor
>>>   */
>>>  const ControlInfoMap &CameraSensor::controls() const
>>> @@ -750,6 +765,11 @@ int CameraSensor::setControls(ControlList *ctrls)
>>>   * Sensor information is only available for raw sensors. When called for a YUV
>>>   * sensor, this function returns -EINVAL.
>>>   *
>>> + * Pipeline handlers that do not change the sensor format using the
>>> + * CameraSensor::setFormat method may need to call
>>> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
>>> + * ranges are up to date.
>>> + *
>>>   * \return 0 on success, a negative error code otherwise
>>>   */
>>>  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
>>> @@ -821,6 +841,15 @@ int CameraSensor::sensorInfo(CameraSensorInfo *info) const
>>>       return 0;
>>>  }
>>>
>>> +/**
>>> + * \fn void CameraSensor::updateControlInfos()
>>> + * \brief Update the sensor's ControlInfos in case they have changed
>>> + */
>>> +void CameraSensor::updateControlInfos()
>>> +{
>>> +     subdev_->updateControlInfos();
>>> +}
>>> +
>>>  std::string CameraSensor::logPrefix() const
>>>  {
>>>       return "'" + entity_->name() + "'";
>>> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
>>> index decd19ef..9678962c 100644
>>> --- a/src/libcamera/v4l2_device.cpp
>>> +++ b/src/libcamera/v4l2_device.cpp
>>> @@ -514,6 +514,32 @@ void V4L2Device::listControls()
>>>       controls_ = std::move(ctrls);
>>>  }
>>>
>>> +/*
>>
>> This is now a public method and needs /**
>> Doesn't doxygen give you a warning here ?
> 
> Ah yes, the documentation seems to have stopped building some time
> ago... there appear to be a couple more dependencies I have to install
> for it to happen now. No problem!

I've discovered today that it's best to build doxygen from source as you
need a very recent version to have a quiet build.

For me that was as straightforward as:

git clone https://github.com/doxygen/doxygen
cd doxygen
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ../ && make -j 8 all install


> Thanks
> David
> 
>>
>> The patch looks good otherwise!
>> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
>>
>> Thanks
>>    j
>>
>>> + * \brief Update the control information that was previously stored by
>>> + * listControls(). Some parts of this information, such as min and max
>>> + * values of some controls, are liable to change when a new format is set.
>>> + */
>>> +void V4L2Device::updateControlInfos()
>>> +{
>>> +     for (auto &controlId : controlIds_) {
>>> +             unsigned int id = controlId->id();
>>> +
>>> +             struct v4l2_query_ext_ctrl ctrl = {};
>>> +             ctrl.id = id;
>>> +
>>> +             if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
>>> +                     LOG(V4L2, Debug)
>>> +                             << "Could not refresh control "
>>> +                             << utils::hex(ctrl.id);
>>> +                     continue;
>>> +             }

I'm curious if 'all' controls need to be refreshed, but I think that's
simpler that trying to filter it with a list that would then bitrot.

So...

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


>>> +
>>> +             /* Assume these are present - listControls() made them. */
>>> +             controlInfo_[id] = ctrl;
>>> +             controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);
>>> +     }
>>> +}
>>> +
>>>  /*
>>>   * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
>>>   * values in \a v4l2Ctrls
>>> --
>>> 2.20.1
>>>
>>> _______________________________________________
>>> libcamera-devel mailing list
>>> libcamera-devel@lists.libcamera.org
>>> https://lists.libcamera.org/listinfo/libcamera-devel
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
>
David Plowman April 21, 2021, 8:56 a.m. UTC | #4
Hi everyone

Just a little nudge on this one... what are folks thinking, is there
more stuff to look at here?

Thanks
David

On Tue, 13 Apr 2021 at 22:34, Kieran Bingham
<kieran.bingham@ideasonboard.com> wrote:
>
> Hi David,
>
> On 07/04/2021 10:33, David Plowman wrote:
> > Hi Jacopo
> >
> > On Tue, 6 Apr 2021 at 13:09, Jacopo Mondi <jacopo@jmondi.org> wrote:
> >>
> >> Hi David,
> >>
> >> On Tue, Apr 06, 2021 at 11:40:49AM +0100, David Plowman wrote:
> >>> The minimum and maximum vblanking can change when a new format is
> >>> applied to the sensor subdevice, so be sure to retrieve up-to-date
> >>> values.
> >>>
> >>> The V4L2Device acquires the new updateControlInfos() method to perform
> >>> this function, and which the CameraSensor calls automatically if its
> >>> setFormat method is used to update the sensor.
> >>>
> >>> However, not all pipeline handlers invoke the setFormat method
> >>> directly, so the new method must be made publicly available for
> >>> pipeline handlers to call if they need to.
> >>>
> >>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> >>> ---
> >>>  include/libcamera/internal/camera_sensor.h |  2 ++
> >>>  include/libcamera/internal/v4l2_device.h   |  2 ++
> >>>  src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
> >>>  src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++
> >>>  4 files changed, 60 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> >>> index 3e98f71b..c94744f0 100644
> >>> --- a/include/libcamera/internal/camera_sensor.h
> >>> +++ b/include/libcamera/internal/camera_sensor.h
> >>> @@ -68,6 +68,8 @@ public:
> >>>       const ControlList &properties() const { return properties_; }
> >>>       int sensorInfo(CameraSensorInfo *info) const;
> >>>
> >>> +     void updateControlInfos();
> >>> +
> >>>  protected:
> >>>       std::string logPrefix() const override;
> >>>
> >>> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
> >>> index d006bf68..274cbe65 100644
> >>> --- a/include/libcamera/internal/v4l2_device.h
> >>> +++ b/include/libcamera/internal/v4l2_device.h
> >>> @@ -41,6 +41,8 @@ public:
> >>>       int setFrameStartEnabled(bool enable);
> >>>       Signal<uint32_t> frameStart;
> >>>
> >>> +     void updateControlInfos();
> >>> +
> >>>  protected:
> >>>       V4L2Device(const std::string &deviceNode);
> >>>       ~V4L2Device();
> >>> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> >>> index f7ed91d9..5dbe47a7 100644
> >>> --- a/src/libcamera/camera_sensor.cpp
> >>> +++ b/src/libcamera/camera_sensor.cpp
> >>> @@ -656,15 +656,30 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
> >>>  /**
> >>>   * \brief Set the sensor output format
> >>>   * \param[in] format The desired sensor output format
> >>> + *
> >>> + * The sensor output format is set. The ranges of any controls associated
> >>> + * with the sensor are also updated.
> >>> + *
> >>>   * \return 0 on success or a negative error code otherwise
> >>>   */
> >>>  int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
> >>>  {
> >>> -     return subdev_->setFormat(pad_, format);
> >>> +     int ret = subdev_->setFormat(pad_, format);
> >>> +
> >>> +     if (ret == 0)
> >>> +             updateControlInfos();
> >>> +
> >>> +     return ret;
> >>>  }
> >>>
> >>>  /**
> >>>   * \brief Retrieve the supported V4L2 controls and their information
> >>> + *
> >>> + * Pipeline handlers that do not change the sensor format using the
> >>> + * CameraSensor::setFormat method may need to call
> >>> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> >>> + * ranges are up to date.
> >>> + *
> >>>   * \return A map of the V4L2 controls supported by the sensor
> >>>   */
> >>>  const ControlInfoMap &CameraSensor::controls() const
> >>> @@ -750,6 +765,11 @@ int CameraSensor::setControls(ControlList *ctrls)
> >>>   * Sensor information is only available for raw sensors. When called for a YUV
> >>>   * sensor, this function returns -EINVAL.
> >>>   *
> >>> + * Pipeline handlers that do not change the sensor format using the
> >>> + * CameraSensor::setFormat method may need to call
> >>> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> >>> + * ranges are up to date.
> >>> + *
> >>>   * \return 0 on success, a negative error code otherwise
> >>>   */
> >>>  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> >>> @@ -821,6 +841,15 @@ int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> >>>       return 0;
> >>>  }
> >>>
> >>> +/**
> >>> + * \fn void CameraSensor::updateControlInfos()
> >>> + * \brief Update the sensor's ControlInfos in case they have changed
> >>> + */
> >>> +void CameraSensor::updateControlInfos()
> >>> +{
> >>> +     subdev_->updateControlInfos();
> >>> +}
> >>> +
> >>>  std::string CameraSensor::logPrefix() const
> >>>  {
> >>>       return "'" + entity_->name() + "'";
> >>> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> >>> index decd19ef..9678962c 100644
> >>> --- a/src/libcamera/v4l2_device.cpp
> >>> +++ b/src/libcamera/v4l2_device.cpp
> >>> @@ -514,6 +514,32 @@ void V4L2Device::listControls()
> >>>       controls_ = std::move(ctrls);
> >>>  }
> >>>
> >>> +/*
> >>
> >> This is now a public method and needs /**
> >> Doesn't doxygen give you a warning here ?
> >
> > Ah yes, the documentation seems to have stopped building some time
> > ago... there appear to be a couple more dependencies I have to install
> > for it to happen now. No problem!
>
> I've discovered today that it's best to build doxygen from source as you
> need a very recent version to have a quiet build.
>
> For me that was as straightforward as:
>
> git clone https://github.com/doxygen/doxygen
> cd doxygen
> mkdir build
> cd build
> cmake -DCMAKE_INSTALL_PREFIX=/usr ../ && make -j 8 all install
>
>
> > Thanks
> > David
> >
> >>
> >> The patch looks good otherwise!
> >> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
> >>
> >> Thanks
> >>    j
> >>
> >>> + * \brief Update the control information that was previously stored by
> >>> + * listControls(). Some parts of this information, such as min and max
> >>> + * values of some controls, are liable to change when a new format is set.
> >>> + */
> >>> +void V4L2Device::updateControlInfos()
> >>> +{
> >>> +     for (auto &controlId : controlIds_) {
> >>> +             unsigned int id = controlId->id();
> >>> +
> >>> +             struct v4l2_query_ext_ctrl ctrl = {};
> >>> +             ctrl.id = id;
> >>> +
> >>> +             if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
> >>> +                     LOG(V4L2, Debug)
> >>> +                             << "Could not refresh control "
> >>> +                             << utils::hex(ctrl.id);
> >>> +                     continue;
> >>> +             }
>
> I'm curious if 'all' controls need to be refreshed, but I think that's
> simpler that trying to filter it with a list that would then bitrot.
>
> So...
>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>
>
> >>> +
> >>> +             /* Assume these are present - listControls() made them. */
> >>> +             controlInfo_[id] = ctrl;
> >>> +             controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);
> >>> +     }
> >>> +}
> >>> +
> >>>  /*
> >>>   * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
> >>>   * values in \a v4l2Ctrls
> >>> --
> >>> 2.20.1
> >>>
> >>> _______________________________________________
> >>> libcamera-devel mailing list
> >>> libcamera-devel@lists.libcamera.org
> >>> https://lists.libcamera.org/listinfo/libcamera-devel
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel@lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
> >
>
> --
> Regards
> --
> Kieran
David Plowman May 4, 2021, 8:19 a.m. UTC | #5
Hi again - can I give this one another little prod just so that it's
not forgotten?

Thanks!
David

On Wed, 21 Apr 2021 at 09:56, David Plowman
<david.plowman@raspberrypi.com> wrote:
>
> Hi everyone
>
> Just a little nudge on this one... what are folks thinking, is there
> more stuff to look at here?
>
> Thanks
> David
>
> On Tue, 13 Apr 2021 at 22:34, Kieran Bingham
> <kieran.bingham@ideasonboard.com> wrote:
> >
> > Hi David,
> >
> > On 07/04/2021 10:33, David Plowman wrote:
> > > Hi Jacopo
> > >
> > > On Tue, 6 Apr 2021 at 13:09, Jacopo Mondi <jacopo@jmondi.org> wrote:
> > >>
> > >> Hi David,
> > >>
> > >> On Tue, Apr 06, 2021 at 11:40:49AM +0100, David Plowman wrote:
> > >>> The minimum and maximum vblanking can change when a new format is
> > >>> applied to the sensor subdevice, so be sure to retrieve up-to-date
> > >>> values.
> > >>>
> > >>> The V4L2Device acquires the new updateControlInfos() method to perform
> > >>> this function, and which the CameraSensor calls automatically if its
> > >>> setFormat method is used to update the sensor.
> > >>>
> > >>> However, not all pipeline handlers invoke the setFormat method
> > >>> directly, so the new method must be made publicly available for
> > >>> pipeline handlers to call if they need to.
> > >>>
> > >>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> > >>> ---
> > >>>  include/libcamera/internal/camera_sensor.h |  2 ++
> > >>>  include/libcamera/internal/v4l2_device.h   |  2 ++
> > >>>  src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
> > >>>  src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++
> > >>>  4 files changed, 60 insertions(+), 1 deletion(-)
> > >>>
> > >>> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> > >>> index 3e98f71b..c94744f0 100644
> > >>> --- a/include/libcamera/internal/camera_sensor.h
> > >>> +++ b/include/libcamera/internal/camera_sensor.h
> > >>> @@ -68,6 +68,8 @@ public:
> > >>>       const ControlList &properties() const { return properties_; }
> > >>>       int sensorInfo(CameraSensorInfo *info) const;
> > >>>
> > >>> +     void updateControlInfos();
> > >>> +
> > >>>  protected:
> > >>>       std::string logPrefix() const override;
> > >>>
> > >>> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
> > >>> index d006bf68..274cbe65 100644
> > >>> --- a/include/libcamera/internal/v4l2_device.h
> > >>> +++ b/include/libcamera/internal/v4l2_device.h
> > >>> @@ -41,6 +41,8 @@ public:
> > >>>       int setFrameStartEnabled(bool enable);
> > >>>       Signal<uint32_t> frameStart;
> > >>>
> > >>> +     void updateControlInfos();
> > >>> +
> > >>>  protected:
> > >>>       V4L2Device(const std::string &deviceNode);
> > >>>       ~V4L2Device();
> > >>> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> > >>> index f7ed91d9..5dbe47a7 100644
> > >>> --- a/src/libcamera/camera_sensor.cpp
> > >>> +++ b/src/libcamera/camera_sensor.cpp
> > >>> @@ -656,15 +656,30 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
> > >>>  /**
> > >>>   * \brief Set the sensor output format
> > >>>   * \param[in] format The desired sensor output format
> > >>> + *
> > >>> + * The sensor output format is set. The ranges of any controls associated
> > >>> + * with the sensor are also updated.
> > >>> + *
> > >>>   * \return 0 on success or a negative error code otherwise
> > >>>   */
> > >>>  int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
> > >>>  {
> > >>> -     return subdev_->setFormat(pad_, format);
> > >>> +     int ret = subdev_->setFormat(pad_, format);
> > >>> +
> > >>> +     if (ret == 0)
> > >>> +             updateControlInfos();
> > >>> +
> > >>> +     return ret;
> > >>>  }
> > >>>
> > >>>  /**
> > >>>   * \brief Retrieve the supported V4L2 controls and their information
> > >>> + *
> > >>> + * Pipeline handlers that do not change the sensor format using the
> > >>> + * CameraSensor::setFormat method may need to call
> > >>> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> > >>> + * ranges are up to date.
> > >>> + *
> > >>>   * \return A map of the V4L2 controls supported by the sensor
> > >>>   */
> > >>>  const ControlInfoMap &CameraSensor::controls() const
> > >>> @@ -750,6 +765,11 @@ int CameraSensor::setControls(ControlList *ctrls)
> > >>>   * Sensor information is only available for raw sensors. When called for a YUV
> > >>>   * sensor, this function returns -EINVAL.
> > >>>   *
> > >>> + * Pipeline handlers that do not change the sensor format using the
> > >>> + * CameraSensor::setFormat method may need to call
> > >>> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> > >>> + * ranges are up to date.
> > >>> + *
> > >>>   * \return 0 on success, a negative error code otherwise
> > >>>   */
> > >>>  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> > >>> @@ -821,6 +841,15 @@ int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> > >>>       return 0;
> > >>>  }
> > >>>
> > >>> +/**
> > >>> + * \fn void CameraSensor::updateControlInfos()
> > >>> + * \brief Update the sensor's ControlInfos in case they have changed
> > >>> + */
> > >>> +void CameraSensor::updateControlInfos()
> > >>> +{
> > >>> +     subdev_->updateControlInfos();
> > >>> +}
> > >>> +
> > >>>  std::string CameraSensor::logPrefix() const
> > >>>  {
> > >>>       return "'" + entity_->name() + "'";
> > >>> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > >>> index decd19ef..9678962c 100644
> > >>> --- a/src/libcamera/v4l2_device.cpp
> > >>> +++ b/src/libcamera/v4l2_device.cpp
> > >>> @@ -514,6 +514,32 @@ void V4L2Device::listControls()
> > >>>       controls_ = std::move(ctrls);
> > >>>  }
> > >>>
> > >>> +/*
> > >>
> > >> This is now a public method and needs /**
> > >> Doesn't doxygen give you a warning here ?
> > >
> > > Ah yes, the documentation seems to have stopped building some time
> > > ago... there appear to be a couple more dependencies I have to install
> > > for it to happen now. No problem!
> >
> > I've discovered today that it's best to build doxygen from source as you
> > need a very recent version to have a quiet build.
> >
> > For me that was as straightforward as:
> >
> > git clone https://github.com/doxygen/doxygen
> > cd doxygen
> > mkdir build
> > cd build
> > cmake -DCMAKE_INSTALL_PREFIX=/usr ../ && make -j 8 all install
> >
> >
> > > Thanks
> > > David
> > >
> > >>
> > >> The patch looks good otherwise!
> > >> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
> > >>
> > >> Thanks
> > >>    j
> > >>
> > >>> + * \brief Update the control information that was previously stored by
> > >>> + * listControls(). Some parts of this information, such as min and max
> > >>> + * values of some controls, are liable to change when a new format is set.
> > >>> + */
> > >>> +void V4L2Device::updateControlInfos()
> > >>> +{
> > >>> +     for (auto &controlId : controlIds_) {
> > >>> +             unsigned int id = controlId->id();
> > >>> +
> > >>> +             struct v4l2_query_ext_ctrl ctrl = {};
> > >>> +             ctrl.id = id;
> > >>> +
> > >>> +             if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
> > >>> +                     LOG(V4L2, Debug)
> > >>> +                             << "Could not refresh control "
> > >>> +                             << utils::hex(ctrl.id);
> > >>> +                     continue;
> > >>> +             }
> >
> > I'm curious if 'all' controls need to be refreshed, but I think that's
> > simpler that trying to filter it with a list that would then bitrot.
> >
> > So...
> >
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >
> >
> > >>> +
> > >>> +             /* Assume these are present - listControls() made them. */
> > >>> +             controlInfo_[id] = ctrl;
> > >>> +             controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);
> > >>> +     }
> > >>> +}
> > >>> +
> > >>>  /*
> > >>>   * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
> > >>>   * values in \a v4l2Ctrls
> > >>> --
> > >>> 2.20.1
> > >>>
> > >>> _______________________________________________
> > >>> libcamera-devel mailing list
> > >>> libcamera-devel@lists.libcamera.org
> > >>> https://lists.libcamera.org/listinfo/libcamera-devel
> > > _______________________________________________
> > > libcamera-devel mailing list
> > > libcamera-devel@lists.libcamera.org
> > > https://lists.libcamera.org/listinfo/libcamera-devel
> > >
> >
> > --
> > Regards
> > --
> > Kieran
Laurent Pinchart May 4, 2021, 12:15 p.m. UTC | #6
Hi David,

Thank you for the patch.

On Tue, Apr 06, 2021 at 11:40:49AM +0100, David Plowman wrote:

Oh my, that's such a long delay :-( Sorry about that.

> The minimum and maximum vblanking can change when a new format is
> applied to the sensor subdevice, so be sure to retrieve up-to-date
> values.
> 
> The V4L2Device acquires the new updateControlInfos() method to perform
> this function, and which the CameraSensor calls automatically if its
> setFormat method is used to update the sensor.
> 
> However, not all pipeline handlers invoke the setFormat method
> directly, so the new method must be made publicly available for
> pipeline handlers to call if they need to.
> 
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  include/libcamera/internal/camera_sensor.h |  2 ++
>  include/libcamera/internal/v4l2_device.h   |  2 ++
>  src/libcamera/camera_sensor.cpp            | 31 +++++++++++++++++++++-
>  src/libcamera/v4l2_device.cpp              | 26 ++++++++++++++++++

I may have split the patch in two, but it's no big deal.

>  4 files changed, 60 insertions(+), 1 deletion(-)
> 
> diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
> index 3e98f71b..c94744f0 100644
> --- a/include/libcamera/internal/camera_sensor.h
> +++ b/include/libcamera/internal/camera_sensor.h
> @@ -68,6 +68,8 @@ public:
>  	const ControlList &properties() const { return properties_; }
>  	int sensorInfo(CameraSensorInfo *info) const;
>  
> +	void updateControlInfos();

Let's name this updateControlInfo() as information is uncountable. Same
below.

> +
>  protected:
>  	std::string logPrefix() const override;
>  
> diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
> index d006bf68..274cbe65 100644
> --- a/include/libcamera/internal/v4l2_device.h
> +++ b/include/libcamera/internal/v4l2_device.h
> @@ -41,6 +41,8 @@ public:
>  	int setFrameStartEnabled(bool enable);
>  	Signal<uint32_t> frameStart;
>  
> +	void updateControlInfos();
> +
>  protected:
>  	V4L2Device(const std::string &deviceNode);
>  	~V4L2Device();
> diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> index f7ed91d9..5dbe47a7 100644
> --- a/src/libcamera/camera_sensor.cpp
> +++ b/src/libcamera/camera_sensor.cpp
> @@ -656,15 +656,30 @@ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
>  /**
>   * \brief Set the sensor output format
>   * \param[in] format The desired sensor output format
> + *
> + * The sensor output format is set. The ranges of any controls associated
> + * with the sensor are also updated.

You can drop the first sentence.

> + *
>   * \return 0 on success or a negative error code otherwise
>   */
>  int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
>  {
> -	return subdev_->setFormat(pad_, format);
> +	int ret = subdev_->setFormat(pad_, format);
> +
> +	if (ret == 0)
> +		updateControlInfos();

We tend to return immediately on error.

	int ret = subdev_->setFormat(pad_, format);
	if (ret)
		return ret;

	updateControlInfos();
	return 0;

> +
> +	return ret;
>  }
>  
>  /**
>   * \brief Retrieve the supported V4L2 controls and their information
> + *
> + * Pipeline handlers that do not change the sensor format using the
> + * CameraSensor::setFormat method may need to call

Just "setFormat()" will be enough for doxygen to pick it up, as we're in
the same class. Same below.

> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> + * ranges are up to date.

I'd go the other way around, saying that the control information is
updated automatically when setFormat() is called.

 * Control information is updated automatically to reflect the current sensor
 * configuration when the setFormat() function is called, without invalidating
 * any iterator on the ControlInfoMap. A manual update can also be forced by
 * calling the updateControlInfo() function for pipeline handlers that change
 * the sensor configuration without using setFormat().

Note that we really don't want to see more pipeline handlers doing this,
and I'd like to deprecate this method in the future and switch to the
regular CameraSensor::setFormat() for the Raspberry Pi pipeline handler
too.

Please also note that we're considering removing the V4L2 dependency in
the API of the CameraSensor class. If that happens, we will really want
to drop the manual updateControlInfo().

> + *
>   * \return A map of the V4L2 controls supported by the sensor
>   */
>  const ControlInfoMap &CameraSensor::controls() const
> @@ -750,6 +765,11 @@ int CameraSensor::setControls(ControlList *ctrls)
>   * Sensor information is only available for raw sensors. When called for a YUV
>   * sensor, this function returns -EINVAL.
>   *
> + * Pipeline handlers that do not change the sensor format using the
> + * CameraSensor::setFormat method may need to call
> + * CameraSensor::updateControlInfos beforehand, to ensure all the control
> + * ranges are up to date.

Based on the comment above, I'm tempted to drop this one.

> + *
>   * \return 0 on success, a negative error code otherwise
>   */
>  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
> @@ -821,6 +841,15 @@ int CameraSensor::sensorInfo(CameraSensorInfo *info) const
>  	return 0;
>  }
>  
> +/**
> + * \fn void CameraSensor::updateControlInfos()
> + * \brief Update the sensor's ControlInfos in case they have changed

It's not ControlInfos but ControlInfoMap. You can add a \sa
V4L2Device::updateControlInfo()

> + */
> +void CameraSensor::updateControlInfos()
> +{
> +	subdev_->updateControlInfos();
> +}
> +
>  std::string CameraSensor::logPrefix() const
>  {
>  	return "'" + entity_->name() + "'";
> diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> index decd19ef..9678962c 100644
> --- a/src/libcamera/v4l2_device.cpp
> +++ b/src/libcamera/v4l2_device.cpp
> @@ -514,6 +514,32 @@ void V4L2Device::listControls()
>  	controls_ = std::move(ctrls);
>  }
>  
> +/*
> + * \brief Update the control information that was previously stored by
> + * listControls(). Some parts of this information, such as min and max
> + * values of some controls, are liable to change when a new format is set.

That's not very brief :-) Let's split it. Also, listControls() being a
private function, it's not great to document a public API based on it.
And finally, we should specify the behaviour more precisely, as we're
updating information live.

 * \brief Update the information for all device controls
 *
 * The V4L2Device class caches information about all controls supported by the
 * device and exposes it through the controls() and controlInfo() functions.
 * Control information may change at runtime, for instance when formats on a
 * subdev are modified. When this occurs, this function can be used to refresh
 * control information. The information is refreshed in-place, all pointers to
 * v4l2_query_ext_ctrl instances previously returned by controlInfo() and
 * iterators to the ControlInfoMap returned by controls() remain valid.
 *
 * Note that control information isn't refreshed automatically is it may be an
 * expensive operation. The V4L2Device users are responsible for calling this
 * function when required, based on their usage pattern of the class.

> + */
> +void V4L2Device::updateControlInfos()
> +{
> +	for (auto &controlId : controlIds_) {
> +		unsigned int id = controlId->id();
> +
> +		struct v4l2_query_ext_ctrl ctrl = {};
> +		ctrl.id = id;
> +
> +		if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
> +			LOG(V4L2, Debug)
> +				<< "Could not refresh control "
> +				<< utils::hex(ctrl.id);

s/ctrl.id/id/

> +			continue;
> +		}
> +
> +		/* Assume these are present - listControls() made them. */
> +		controlInfo_[id] = ctrl;
> +		controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);

That's two lookups for each entry, I think we can do better by iterating
over controls_ instead of controlIds_. It could also be possibly to
avoid the copy by operating on the controlInfo_ entry directly.

	for (auto &[controlId, info] : controls_) {
		unsigned int id = controlId->id();

		/*
		 * Assume controlInfo_ has a corresponding entry, as it has been
		 * generated by listControls().
		 */
		struct v4l2_query_ext_ctrl &ctrl = controlInfo_[id];

		if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
			LOG(V4L2, Debug)
				<< "Could not refresh control "
				<< utils::hex(id);
			continue;
		}

		info = V4L2ControlInfo(ctrl);
	}

> +	}
> +}
> +
>  /*
>   * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
>   * values in \a v4l2Ctrls

Patch
diff mbox series

diff --git a/include/libcamera/internal/camera_sensor.h b/include/libcamera/internal/camera_sensor.h
index 3e98f71b..c94744f0 100644
--- a/include/libcamera/internal/camera_sensor.h
+++ b/include/libcamera/internal/camera_sensor.h
@@ -68,6 +68,8 @@  public:
 	const ControlList &properties() const { return properties_; }
 	int sensorInfo(CameraSensorInfo *info) const;
 
+	void updateControlInfos();
+
 protected:
 	std::string logPrefix() const override;
 
diff --git a/include/libcamera/internal/v4l2_device.h b/include/libcamera/internal/v4l2_device.h
index d006bf68..274cbe65 100644
--- a/include/libcamera/internal/v4l2_device.h
+++ b/include/libcamera/internal/v4l2_device.h
@@ -41,6 +41,8 @@  public:
 	int setFrameStartEnabled(bool enable);
 	Signal<uint32_t> frameStart;
 
+	void updateControlInfos();
+
 protected:
 	V4L2Device(const std::string &deviceNode);
 	~V4L2Device();
diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
index f7ed91d9..5dbe47a7 100644
--- a/src/libcamera/camera_sensor.cpp
+++ b/src/libcamera/camera_sensor.cpp
@@ -656,15 +656,30 @@  V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbu
 /**
  * \brief Set the sensor output format
  * \param[in] format The desired sensor output format
+ *
+ * The sensor output format is set. The ranges of any controls associated
+ * with the sensor are also updated.
+ *
  * \return 0 on success or a negative error code otherwise
  */
 int CameraSensor::setFormat(V4L2SubdeviceFormat *format)
 {
-	return subdev_->setFormat(pad_, format);
+	int ret = subdev_->setFormat(pad_, format);
+
+	if (ret == 0)
+		updateControlInfos();
+
+	return ret;
 }
 
 /**
  * \brief Retrieve the supported V4L2 controls and their information
+ *
+ * Pipeline handlers that do not change the sensor format using the
+ * CameraSensor::setFormat method may need to call
+ * CameraSensor::updateControlInfos beforehand, to ensure all the control
+ * ranges are up to date.
+ *
  * \return A map of the V4L2 controls supported by the sensor
  */
 const ControlInfoMap &CameraSensor::controls() const
@@ -750,6 +765,11 @@  int CameraSensor::setControls(ControlList *ctrls)
  * Sensor information is only available for raw sensors. When called for a YUV
  * sensor, this function returns -EINVAL.
  *
+ * Pipeline handlers that do not change the sensor format using the
+ * CameraSensor::setFormat method may need to call
+ * CameraSensor::updateControlInfos beforehand, to ensure all the control
+ * ranges are up to date.
+ *
  * \return 0 on success, a negative error code otherwise
  */
 int CameraSensor::sensorInfo(CameraSensorInfo *info) const
@@ -821,6 +841,15 @@  int CameraSensor::sensorInfo(CameraSensorInfo *info) const
 	return 0;
 }
 
+/**
+ * \fn void CameraSensor::updateControlInfos()
+ * \brief Update the sensor's ControlInfos in case they have changed
+ */
+void CameraSensor::updateControlInfos()
+{
+	subdev_->updateControlInfos();
+}
+
 std::string CameraSensor::logPrefix() const
 {
 	return "'" + entity_->name() + "'";
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index decd19ef..9678962c 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -514,6 +514,32 @@  void V4L2Device::listControls()
 	controls_ = std::move(ctrls);
 }
 
+/*
+ * \brief Update the control information that was previously stored by
+ * listControls(). Some parts of this information, such as min and max
+ * values of some controls, are liable to change when a new format is set.
+ */
+void V4L2Device::updateControlInfos()
+{
+	for (auto &controlId : controlIds_) {
+		unsigned int id = controlId->id();
+
+		struct v4l2_query_ext_ctrl ctrl = {};
+		ctrl.id = id;
+
+		if (ioctl(VIDIOC_QUERY_EXT_CTRL, &ctrl)) {
+			LOG(V4L2, Debug)
+				<< "Could not refresh control "
+				<< utils::hex(ctrl.id);
+			continue;
+		}
+
+		/* Assume these are present - listControls() made them. */
+		controlInfo_[id] = ctrl;
+		controls_.at(controlId.get()) = V4L2ControlInfo(ctrl);
+	}
+}
+
 /*
  * \brief Update the value of the first \a count V4L2 controls in \a ctrls using
  * values in \a v4l2Ctrls