[libcamera-devel,v2,2/3] ipa: raspberrypi: Better heruistics for calculating Unicam timeout
diff mbox series

Message ID mailman.57.1677765273.25031.libcamera-devel@lists.libcamera.org
State Superseded
Headers show
Series
  • Raspberry Pi: Improving camera timeouts
Related show

Commit Message

Naushir Patuck March 2, 2023, 1:54 p.m. UTC
The existing mechanism of setting a timeout value simply uses the
maximum possible frame length advertised by the sensor mode. This can be
problematic when, for example, the IMX477 sensor can use a frame length
of over 600 seconds. However, for typical usage the frame length will
never go over several 100s of milliseconds, making the timeout very
impractical.

Store a list of the last 10 frame length values requested by the AGC. On
startup, and at the end of every frame, take the maximum frame length
value from this list and return that to the pipeline handler through the
setCameraTimeoutValue() signal. This allows the timeout value to better
track the actual sensor usage.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
---
 src/ipa/raspberrypi/raspberrypi.cpp | 44 +++++++++++++++++++++++++++--
 1 file changed, 41 insertions(+), 3 deletions(-)

Comments

Jacopo Mondi March 5, 2023, 11:37 a.m. UTC | #1
Hi Naush

On Thu, Mar 02, 2023 at 01:54:28PM +0000, Naushir Patuck via libcamera-devel wrote:
> Date: Thu,  2 Mar 2023 13:54:28 +0000
> From: Naushir Patuck <naush@raspberrypi.com>
> To: libcamera-devel@lists.libcamera.org
> Subject: [PATCH v2 2/3] ipa: raspberrypi: Better heruistics for calculating
>  Unicam timeout
> X-Mailer: git-send-email 2.34.1
>
> The existing mechanism of setting a timeout value simply uses the
> maximum possible frame length advertised by the sensor mode. This can be
> problematic when, for example, the IMX477 sensor can use a frame length
> of over 600 seconds. However, for typical usage the frame length will
> never go over several 100s of milliseconds, making the timeout very
> impractical.
>
> Store a list of the last 10 frame length values requested by the AGC. On
> startup, and at the end of every frame, take the maximum frame length
> value from this list and return that to the pipeline handler through the
> setCameraTimeoutValue() signal. This allows the timeout value to better
> track the actual sensor usage.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>

Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

> ---
>  src/ipa/raspberrypi/raspberrypi.cpp | 44 +++++++++++++++++++++++++++--
>  1 file changed, 41 insertions(+), 3 deletions(-)
>
> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> index f6826bf27fe1..7b5aed644a67 100644
> --- a/src/ipa/raspberrypi/raspberrypi.cpp
> +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> @@ -8,6 +8,7 @@
>  #include <algorithm>
>  #include <array>
>  #include <cstring>
> +#include <deque>
>  #include <fcntl.h>
>  #include <math.h>
>  #include <stdint.h>
> @@ -64,6 +65,9 @@ using utils::Duration;
>  /* Number of metadata objects available in the context list. */
>  constexpr unsigned int numMetadataContexts = 16;
>
> +/* Number of frame length times to hold in the queue. */
> +constexpr unsigned int FrameLengthsQueueSize = 10;
> +
>  /* Configure the sensor with these values initially. */
>  constexpr double defaultAnalogueGain = 1.0;
>  constexpr Duration defaultExposureTime = 20.0ms;
> @@ -121,7 +125,8 @@ class IPARPi : public IPARPiInterface
>  public:
>  	IPARPi()
>  		: controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0),
> -		  lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true)
> +		  lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true),
> +		  lastTimeout_(0s)
>  	{
>  	}
>
> @@ -155,6 +160,7 @@ private:
>  	void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
>  	RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const;
>  	void processStats(unsigned int bufferId, unsigned int ipaContext);
> +	void setCameraTimeoutValue();
>  	void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
>  	void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
>  	void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
> @@ -220,6 +226,10 @@ private:
>
>  	/* Maximum gain code for the sensor. */
>  	uint32_t maxSensorGainCode_;
> +
> +	/* Track the frame length times over FrameLengthsQueueSize frames. */
> +	std::deque<Duration> frameLengths_;
> +	Duration lastTimeout_;
>  };
>
>  int IPARPi::init(const IPASettings &settings, bool lensPresent, IPAInitResult *result)
> @@ -284,6 +294,10 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
>
>  	controller_.switchMode(mode_, &metadata);
>
> +	/* Reset the frame lengths queue */
> +	frameLengths_.clear();
> +	frameLengths_.resize(FrameLengthsQueueSize, 0s);
> +
>  	/* SwitchMode may supply updated exposure/gain values to use. */
>  	AgcStatus agcStatus;
>  	agcStatus.shutterTime = 0.0s;
> @@ -294,6 +308,7 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
>  		ControlList ctrls(sensorCtrls_);
>  		applyAGC(&agcStatus, ctrls);
>  		startConfig->controls = std::move(ctrls);
> +		setCameraTimeoutValue();
>  	}
>
>  	/*
> @@ -340,8 +355,6 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
>  	}
>
>  	startConfig->dropFrameCount = dropFrameCount_;
> -	const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength;
> -	setCameraTimeout.emit(maxSensorFrameDuration.get<std::milli>());
>
>  	firstStart_ = false;
>  	lastRunTimestamp_ = 0;
> @@ -1434,6 +1447,22 @@ void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
>  		applyAGC(&agcStatus, ctrls);
>
>  		setDelayedControls.emit(ctrls, ipaContext);
> +		setCameraTimeoutValue();
> +	}
> +}
> +
> +void IPARPi::setCameraTimeoutValue()
> +{
> +	/*
> +	 * Take the maximum value of the exposure queue as the camera timeout
> +	 * value to pass back to the pipeline handler. Only signal if it has changed
> +	 * from the last set value.
> +	 */
> +	auto max = std::max_element(frameLengths_.begin(), frameLengths_.end());
> +
> +	if (*max != lastTimeout_) {
> +		setCameraTimeout.emit(max->get<std::milli>());
> +		lastTimeout_ = *max;
>  	}
>  }
>
> @@ -1522,6 +1551,15 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
>  	 */
>  	if (mode_.minLineLength != mode_.maxLineLength)
>  		ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank));
> +
> +	/*
> +	 * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize
> +	 * elements. This will be used to advertise a camera timeout value to the
> +	 * pipeline handler.
> +	 */
> +	frameLengths_.pop_front();
> +	frameLengths_.push_back(helper_->exposure(vblank + mode_.height,
> +						  helper_->hblankToLineLength(hblank)));
>  }
>
>  void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
> --
> 2.34.1
>
Laurent Pinchart March 6, 2023, 5:25 p.m. UTC | #2
Hi Naush,

Thank you for the patch.

On Thu, Mar 02, 2023 at 01:54:28PM +0000, Naushir Patuck via libcamera-devel wrote:
> 
> The existing mechanism of setting a timeout value simply uses the
> maximum possible frame length advertised by the sensor mode. This can be
> problematic when, for example, the IMX477 sensor can use a frame length
> of over 600 seconds. However, for typical usage the frame length will
> never go over several 100s of milliseconds, making the timeout very
> impractical.
> 
> Store a list of the last 10 frame length values requested by the AGC. On
> startup, and at the end of every frame, take the maximum frame length
> value from this list and return that to the pipeline handler through the
> setCameraTimeoutValue() signal. This allows the timeout value to better
> track the actual sensor usage.
> 
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  src/ipa/raspberrypi/raspberrypi.cpp | 44 +++++++++++++++++++++++++++--
>  1 file changed, 41 insertions(+), 3 deletions(-)
> 
> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> index f6826bf27fe1..7b5aed644a67 100644
> --- a/src/ipa/raspberrypi/raspberrypi.cpp
> +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> @@ -8,6 +8,7 @@
>  #include <algorithm>
>  #include <array>
>  #include <cstring>
> +#include <deque>
>  #include <fcntl.h>
>  #include <math.h>
>  #include <stdint.h>
> @@ -64,6 +65,9 @@ using utils::Duration;
>  /* Number of metadata objects available in the context list. */
>  constexpr unsigned int numMetadataContexts = 16;
>  
> +/* Number of frame length times to hold in the queue. */
> +constexpr unsigned int FrameLengthsQueueSize = 10;
> +
>  /* Configure the sensor with these values initially. */
>  constexpr double defaultAnalogueGain = 1.0;
>  constexpr Duration defaultExposureTime = 20.0ms;
> @@ -121,7 +125,8 @@ class IPARPi : public IPARPiInterface
>  public:
>  	IPARPi()
>  		: controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0),
> -		  lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true)
> +		  lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true),
> +		  lastTimeout_(0s)
>  	{
>  	}
>  
> @@ -155,6 +160,7 @@ private:
>  	void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
>  	RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const;
>  	void processStats(unsigned int bufferId, unsigned int ipaContext);
> +	void setCameraTimeoutValue();
>  	void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
>  	void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
>  	void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
> @@ -220,6 +226,10 @@ private:
>  
>  	/* Maximum gain code for the sensor. */
>  	uint32_t maxSensorGainCode_;
> +
> +	/* Track the frame length times over FrameLengthsQueueSize frames. */
> +	std::deque<Duration> frameLengths_;

Another note to self: create a generic ring buffer class.

> +	Duration lastTimeout_;
>  };
>  
>  int IPARPi::init(const IPASettings &settings, bool lensPresent, IPAInitResult *result)
> @@ -284,6 +294,10 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
>  
>  	controller_.switchMode(mode_, &metadata);
>  
> +	/* Reset the frame lengths queue */

s/queue/queue./

> +	frameLengths_.clear();
> +	frameLengths_.resize(FrameLengthsQueueSize, 0s);

Should you also reset lastTimeout_ to 0s to ensure a consistent
behaviour at start time ?

> +
>  	/* SwitchMode may supply updated exposure/gain values to use. */
>  	AgcStatus agcStatus;
>  	agcStatus.shutterTime = 0.0s;
> @@ -294,6 +308,7 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
>  		ControlList ctrls(sensorCtrls_);
>  		applyAGC(&agcStatus, ctrls);
>  		startConfig->controls = std::move(ctrls);
> +		setCameraTimeoutValue();
>  	}
>  
>  	/*
> @@ -340,8 +355,6 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
>  	}
>  
>  	startConfig->dropFrameCount = dropFrameCount_;
> -	const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength;
> -	setCameraTimeout.emit(maxSensorFrameDuration.get<std::milli>());
>  
>  	firstStart_ = false;
>  	lastRunTimestamp_ = 0;
> @@ -1434,6 +1447,22 @@ void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)

Adding more context:

	if (agcStatus.shutterTime && agcStatus.analogueGain) {
		ControlList ctrls(sensorCtrls_);

>  		applyAGC(&agcStatus, ctrls);
>  
>  		setDelayedControls.emit(ctrls, ipaContext);
> +		setCameraTimeoutValue();

If the above condition is false, setCameraTimeoutValue() won't be
called. When can that happen, and could it cause any issue ?

> +	}
> +}
> +
> +void IPARPi::setCameraTimeoutValue()
> +{
> +	/*
> +	 * Take the maximum value of the exposure queue as the camera timeout
> +	 * value to pass back to the pipeline handler. Only signal if it has changed

Line wrap.

> +	 * from the last set value.
> +	 */
> +	auto max = std::max_element(frameLengths_.begin(), frameLengths_.end());
> +
> +	if (*max != lastTimeout_) {
> +		setCameraTimeout.emit(max->get<std::milli>());
> +		lastTimeout_ = *max;
>  	}
>  }
>  
> @@ -1522,6 +1551,15 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
>  	 */
>  	if (mode_.minLineLength != mode_.maxLineLength)
>  		ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank));
> +
> +	/*
> +	 * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize
> +	 * elements. This will be used to advertise a camera timeout value to the
> +	 * pipeline handler.

Line wraps here too.

> +	 */
> +	frameLengths_.pop_front();
> +	frameLengths_.push_back(helper_->exposure(vblank + mode_.height,
> +						  helper_->hblankToLineLength(hblank)));
>  }
>  
>  void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
Naushir Patuck March 7, 2023, 8:45 a.m. UTC | #3
Hi Laurent,

Thank you for your feedback.


On Mon, 6 Mar 2023 at 17:25, Laurent Pinchart <
laurent.pinchart@ideasonboard.com> wrote:

> Hi Naush,
>
> Thank you for the patch.
>
> On Thu, Mar 02, 2023 at 01:54:28PM +0000, Naushir Patuck via
> libcamera-devel wrote:
> >
> > The existing mechanism of setting a timeout value simply uses the
> > maximum possible frame length advertised by the sensor mode. This can be
> > problematic when, for example, the IMX477 sensor can use a frame length
> > of over 600 seconds. However, for typical usage the frame length will
> > never go over several 100s of milliseconds, making the timeout very
> > impractical.
> >
> > Store a list of the last 10 frame length values requested by the AGC. On
> > startup, and at the end of every frame, take the maximum frame length
> > value from this list and return that to the pipeline handler through the
> > setCameraTimeoutValue() signal. This allows the timeout value to better
> > track the actual sensor usage.
> >
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> > ---
> >  src/ipa/raspberrypi/raspberrypi.cpp | 44 +++++++++++++++++++++++++++--
> >  1 file changed, 41 insertions(+), 3 deletions(-)
> >
> > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp
> b/src/ipa/raspberrypi/raspberrypi.cpp
> > index f6826bf27fe1..7b5aed644a67 100644
> > --- a/src/ipa/raspberrypi/raspberrypi.cpp
> > +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> > @@ -8,6 +8,7 @@
> >  #include <algorithm>
> >  #include <array>
> >  #include <cstring>
> > +#include <deque>
> >  #include <fcntl.h>
> >  #include <math.h>
> >  #include <stdint.h>
> > @@ -64,6 +65,9 @@ using utils::Duration;
> >  /* Number of metadata objects available in the context list. */
> >  constexpr unsigned int numMetadataContexts = 16;
> >
> > +/* Number of frame length times to hold in the queue. */
> > +constexpr unsigned int FrameLengthsQueueSize = 10;
> > +
> >  /* Configure the sensor with these values initially. */
> >  constexpr double defaultAnalogueGain = 1.0;
> >  constexpr Duration defaultExposureTime = 20.0ms;
> > @@ -121,7 +125,8 @@ class IPARPi : public IPARPiInterface
> >  public:
> >       IPARPi()
> >               : controller_(), frameCount_(0), checkCount_(0),
> mistrustCount_(0),
> > -               lastRunTimestamp_(0), lsTable_(nullptr),
> firstStart_(true)
> > +               lastRunTimestamp_(0), lsTable_(nullptr),
> firstStart_(true),
> > +               lastTimeout_(0s)
> >       {
> >       }
> >
> > @@ -155,6 +160,7 @@ private:
> >       void fillDeviceStatus(const ControlList &sensorControls, unsigned
> int ipaContext);
> >       RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats
> *stats) const;
> >       void processStats(unsigned int bufferId, unsigned int ipaContext);
> > +     void setCameraTimeoutValue();
> >       void applyFrameDurations(Duration minFrameDuration, Duration
> maxFrameDuration);
> >       void applyAGC(const struct AgcStatus *agcStatus, ControlList
> &ctrls);
> >       void applyAWB(const struct AwbStatus *awbStatus, ControlList
> &ctrls);
> > @@ -220,6 +226,10 @@ private:
> >
> >       /* Maximum gain code for the sensor. */
> >       uint32_t maxSensorGainCode_;
> > +
> > +     /* Track the frame length times over FrameLengthsQueueSize frames.
> */
> > +     std::deque<Duration> frameLengths_;
>
> Another note to self: create a generic ring buffer class.
>
> > +     Duration lastTimeout_;
> >  };
> >
> >  int IPARPi::init(const IPASettings &settings, bool lensPresent,
> IPAInitResult *result)
> > @@ -284,6 +294,10 @@ void IPARPi::start(const ControlList &controls,
> StartConfig *startConfig)
> >
> >       controller_.switchMode(mode_, &metadata);
> >
> > +     /* Reset the frame lengths queue */
>
> s/queue/queue./
>
> > +     frameLengths_.clear();
> > +     frameLengths_.resize(FrameLengthsQueueSize, 0s);
>
> Should you also reset lastTimeout_ to 0s to ensure a consistent
> behaviour at start time ?
>

Good catch, I will reset lastTimeout_ here.


>
> > +
> >       /* SwitchMode may supply updated exposure/gain values to use. */
> >       AgcStatus agcStatus;
> >       agcStatus.shutterTime = 0.0s;
> > @@ -294,6 +308,7 @@ void IPARPi::start(const ControlList &controls,
> StartConfig *startConfig)
> >               ControlList ctrls(sensorCtrls_);
> >               applyAGC(&agcStatus, ctrls);
> >               startConfig->controls = std::move(ctrls);
> > +             setCameraTimeoutValue();
> >       }
> >
> >       /*
> > @@ -340,8 +355,6 @@ void IPARPi::start(const ControlList &controls,
> StartConfig *startConfig)
> >       }
> >
> >       startConfig->dropFrameCount = dropFrameCount_;
> > -     const Duration maxSensorFrameDuration = mode_.maxFrameLength *
> mode_.maxLineLength;
> > -     setCameraTimeout.emit(maxSensorFrameDuration.get<std::milli>());
> >
> >       firstStart_ = false;
> >       lastRunTimestamp_ = 0;
> > @@ -1434,6 +1447,22 @@ void IPARPi::processStats(unsigned int bufferId,
> unsigned int ipaContext)
>
> Adding more context:
>
>         if (agcStatus.shutterTime && agcStatus.analogueGain) {
>                 ControlList ctrls(sensorCtrls_);
>
> >               applyAGC(&agcStatus, ctrls);
> >
> >               setDelayedControls.emit(ctrls, ipaContext);
> > +             setCameraTimeoutValue();
>
> If the above condition is false, setCameraTimeoutValue() won't be
> called. When can that happen, and could it cause any issue ?
>

Short answer is no.  Really the test is a bit redundant, agc can never ever
return with agcStatus.shutterTime or agcStatus.analogueGain set to 0.  I
ought
to remove that if () clause in a future tidy up.

Regards,
Naush


>
> > +     }
> > +}
> > +
> > +void IPARPi::setCameraTimeoutValue()
> > +{
> > +     /*
> > +      * Take the maximum value of the exposure queue as the camera
> timeout
> > +      * value to pass back to the pipeline handler. Only signal if it
> has changed
>
> Line wrap.
>
> > +      * from the last set value.
> > +      */
> > +     auto max = std::max_element(frameLengths_.begin(),
> frameLengths_.end());
> > +
> > +     if (*max != lastTimeout_) {
> > +             setCameraTimeout.emit(max->get<std::milli>());
> > +             lastTimeout_ = *max;
> >       }
> >  }
> >
> > @@ -1522,6 +1551,15 @@ void IPARPi::applyAGC(const struct AgcStatus
> *agcStatus, ControlList &ctrls)
> >        */
> >       if (mode_.minLineLength != mode_.maxLineLength)
> >               ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank));
> > +
> > +     /*
> > +      * Store the frame length times in a circular queue, up-to
> FrameLengthsQueueSize
> > +      * elements. This will be used to advertise a camera timeout value
> to the
> > +      * pipeline handler.
>
> Line wraps here too.
>
> > +      */
> > +     frameLengths_.pop_front();
> > +     frameLengths_.push_back(helper_->exposure(vblank + mode_.height,
> > +
>  helper_->hblankToLineLength(hblank)));
> >  }
> >
> >  void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList
> &ctrls)
>
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart March 7, 2023, 10:01 a.m. UTC | #4
Hi Naush,

Btw, I just noticed the "heruistics" typo in the subject line.

On Tue, Mar 07, 2023 at 08:45:44AM +0000, Naushir Patuck wrote:
> On Mon, 6 Mar 2023 at 17:25, Laurent Pinchart wrote:
> 
> > Hi Naush,
> >
> > Thank you for the patch.
> >
> > On Thu, Mar 02, 2023 at 01:54:28PM +0000, Naushir Patuck via
> > libcamera-devel wrote:
> > >
> > > The existing mechanism of setting a timeout value simply uses the
> > > maximum possible frame length advertised by the sensor mode. This can be
> > > problematic when, for example, the IMX477 sensor can use a frame length
> > > of over 600 seconds. However, for typical usage the frame length will
> > > never go over several 100s of milliseconds, making the timeout very
> > > impractical.
> > >
> > > Store a list of the last 10 frame length values requested by the AGC. On
> > > startup, and at the end of every frame, take the maximum frame length
> > > value from this list and return that to the pipeline handler through the
> > > setCameraTimeoutValue() signal. This allows the timeout value to better
> > > track the actual sensor usage.
> > >
> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
> > > ---
> > >  src/ipa/raspberrypi/raspberrypi.cpp | 44 +++++++++++++++++++++++++++--
> > >  1 file changed, 41 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
> > > index f6826bf27fe1..7b5aed644a67 100644
> > > --- a/src/ipa/raspberrypi/raspberrypi.cpp
> > > +++ b/src/ipa/raspberrypi/raspberrypi.cpp
> > > @@ -8,6 +8,7 @@
> > >  #include <algorithm>
> > >  #include <array>
> > >  #include <cstring>
> > > +#include <deque>
> > >  #include <fcntl.h>
> > >  #include <math.h>
> > >  #include <stdint.h>
> > > @@ -64,6 +65,9 @@ using utils::Duration;
> > >  /* Number of metadata objects available in the context list. */
> > >  constexpr unsigned int numMetadataContexts = 16;
> > >
> > > +/* Number of frame length times to hold in the queue. */
> > > +constexpr unsigned int FrameLengthsQueueSize = 10;
> > > +
> > >  /* Configure the sensor with these values initially. */
> > >  constexpr double defaultAnalogueGain = 1.0;
> > >  constexpr Duration defaultExposureTime = 20.0ms;
> > > @@ -121,7 +125,8 @@ class IPARPi : public IPARPiInterface
> > >  public:
> > >       IPARPi()
> > >               : controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0),
> > > -               lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true)
> > > +               lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true),
> > > +               lastTimeout_(0s)
> > >       {
> > >       }
> > >
> > > @@ -155,6 +160,7 @@ private:
> > >       void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
> > >       RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const;
> > >       void processStats(unsigned int bufferId, unsigned int ipaContext);
> > > +     void setCameraTimeoutValue();
> > >       void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
> > >       void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
> > >       void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
> > > @@ -220,6 +226,10 @@ private:
> > >
> > >       /* Maximum gain code for the sensor. */
> > >       uint32_t maxSensorGainCode_;
> > > +
> > > +     /* Track the frame length times over FrameLengthsQueueSize frames.
> > */
> > > +     std::deque<Duration> frameLengths_;
> >
> > Another note to self: create a generic ring buffer class.
> >
> > > +     Duration lastTimeout_;
> > >  };
> > >
> > >  int IPARPi::init(const IPASettings &settings, bool lensPresent, IPAInitResult *result)
> > > @@ -284,6 +294,10 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
> > >
> > >       controller_.switchMode(mode_, &metadata);
> > >
> > > +     /* Reset the frame lengths queue */
> >
> > s/queue/queue./
> >
> > > +     frameLengths_.clear();
> > > +     frameLengths_.resize(FrameLengthsQueueSize, 0s);
> >
> > Should you also reset lastTimeout_ to 0s to ensure a consistent
> > behaviour at start time ?
> 
> Good catch, I will reset lastTimeout_ here.
> 
> > > +
> > >       /* SwitchMode may supply updated exposure/gain values to use. */
> > >       AgcStatus agcStatus;
> > >       agcStatus.shutterTime = 0.0s;
> > > @@ -294,6 +308,7 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
> > >               ControlList ctrls(sensorCtrls_);
> > >               applyAGC(&agcStatus, ctrls);
> > >               startConfig->controls = std::move(ctrls);
> > > +             setCameraTimeoutValue();
> > >       }
> > >
> > >       /*
> > > @@ -340,8 +355,6 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
> > >       }
> > >
> > >       startConfig->dropFrameCount = dropFrameCount_;
> > > -     const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength;
> > > -     setCameraTimeout.emit(maxSensorFrameDuration.get<std::milli>());
> > >
> > >       firstStart_ = false;
> > >       lastRunTimestamp_ = 0;
> > > @@ -1434,6 +1447,22 @@ void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
> >
> > Adding more context:
> >
> >         if (agcStatus.shutterTime && agcStatus.analogueGain) {
> >                 ControlList ctrls(sensorCtrls_);
> >
> > >               applyAGC(&agcStatus, ctrls);
> > >
> > >               setDelayedControls.emit(ctrls, ipaContext);
> > > +             setCameraTimeoutValue();
> >
> > If the above condition is false, setCameraTimeoutValue() won't be
> > called. When can that happen, and could it cause any issue ?
> 
> Short answer is no.  Really the test is a bit redundant, agc can never ever
> return with agcStatus.shutterTime or agcStatus.analogueGain set to 0. I ought
> to remove that if () clause in a future tidy up.

That would be nice, thanks.

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

with the other issues fixed.

> > > +     }
> > > +}
> > > +
> > > +void IPARPi::setCameraTimeoutValue()
> > > +{
> > > +     /*
> > > +      * Take the maximum value of the exposure queue as the camera timeout
> > > +      * value to pass back to the pipeline handler. Only signal if it has changed
> >
> > Line wrap.
> >
> > > +      * from the last set value.
> > > +      */
> > > +     auto max = std::max_element(frameLengths_.begin(), frameLengths_.end());
> > > +
> > > +     if (*max != lastTimeout_) {
> > > +             setCameraTimeout.emit(max->get<std::milli>());
> > > +             lastTimeout_ = *max;
> > >       }
> > >  }
> > >
> > > @@ -1522,6 +1551,15 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
> > >        */
> > >       if (mode_.minLineLength != mode_.maxLineLength)
> > >               ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank));
> > > +
> > > +     /*
> > > +      * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize
> > > +      * elements. This will be used to advertise a camera timeout value to the
> > > +      * pipeline handler.
> >
> > Line wraps here too.
> >
> > > +      */
> > > +     frameLengths_.pop_front();
> > > +     frameLengths_.push_back(helper_->exposure(vblank + mode_.height,
> > > +                             helper_->hblankToLineLength(hblank)));
> > >  }
> > >
> > >  void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)

Patch
diff mbox series

diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp
index f6826bf27fe1..7b5aed644a67 100644
--- a/src/ipa/raspberrypi/raspberrypi.cpp
+++ b/src/ipa/raspberrypi/raspberrypi.cpp
@@ -8,6 +8,7 @@ 
 #include <algorithm>
 #include <array>
 #include <cstring>
+#include <deque>
 #include <fcntl.h>
 #include <math.h>
 #include <stdint.h>
@@ -64,6 +65,9 @@  using utils::Duration;
 /* Number of metadata objects available in the context list. */
 constexpr unsigned int numMetadataContexts = 16;
 
+/* Number of frame length times to hold in the queue. */
+constexpr unsigned int FrameLengthsQueueSize = 10;
+
 /* Configure the sensor with these values initially. */
 constexpr double defaultAnalogueGain = 1.0;
 constexpr Duration defaultExposureTime = 20.0ms;
@@ -121,7 +125,8 @@  class IPARPi : public IPARPiInterface
 public:
 	IPARPi()
 		: controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0),
-		  lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true)
+		  lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true),
+		  lastTimeout_(0s)
 	{
 	}
 
@@ -155,6 +160,7 @@  private:
 	void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext);
 	RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const;
 	void processStats(unsigned int bufferId, unsigned int ipaContext);
+	void setCameraTimeoutValue();
 	void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration);
 	void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls);
 	void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls);
@@ -220,6 +226,10 @@  private:
 
 	/* Maximum gain code for the sensor. */
 	uint32_t maxSensorGainCode_;
+
+	/* Track the frame length times over FrameLengthsQueueSize frames. */
+	std::deque<Duration> frameLengths_;
+	Duration lastTimeout_;
 };
 
 int IPARPi::init(const IPASettings &settings, bool lensPresent, IPAInitResult *result)
@@ -284,6 +294,10 @@  void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
 
 	controller_.switchMode(mode_, &metadata);
 
+	/* Reset the frame lengths queue */
+	frameLengths_.clear();
+	frameLengths_.resize(FrameLengthsQueueSize, 0s);
+
 	/* SwitchMode may supply updated exposure/gain values to use. */
 	AgcStatus agcStatus;
 	agcStatus.shutterTime = 0.0s;
@@ -294,6 +308,7 @@  void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
 		ControlList ctrls(sensorCtrls_);
 		applyAGC(&agcStatus, ctrls);
 		startConfig->controls = std::move(ctrls);
+		setCameraTimeoutValue();
 	}
 
 	/*
@@ -340,8 +355,6 @@  void IPARPi::start(const ControlList &controls, StartConfig *startConfig)
 	}
 
 	startConfig->dropFrameCount = dropFrameCount_;
-	const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength;
-	setCameraTimeout.emit(maxSensorFrameDuration.get<std::milli>());
 
 	firstStart_ = false;
 	lastRunTimestamp_ = 0;
@@ -1434,6 +1447,22 @@  void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext)
 		applyAGC(&agcStatus, ctrls);
 
 		setDelayedControls.emit(ctrls, ipaContext);
+		setCameraTimeoutValue();
+	}
+}
+
+void IPARPi::setCameraTimeoutValue()
+{
+	/*
+	 * Take the maximum value of the exposure queue as the camera timeout
+	 * value to pass back to the pipeline handler. Only signal if it has changed
+	 * from the last set value.
+	 */
+	auto max = std::max_element(frameLengths_.begin(), frameLengths_.end());
+
+	if (*max != lastTimeout_) {
+		setCameraTimeout.emit(max->get<std::milli>());
+		lastTimeout_ = *max;
 	}
 }
 
@@ -1522,6 +1551,15 @@  void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)
 	 */
 	if (mode_.minLineLength != mode_.maxLineLength)
 		ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank));
+
+	/*
+	 * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize
+	 * elements. This will be used to advertise a camera timeout value to the
+	 * pipeline handler.
+	 */
+	frameLengths_.pop_front();
+	frameLengths_.push_back(helper_->exposure(vblank + mode_.height,
+						  helper_->hblankToLineLength(hblank)));
 }
 
 void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)