@@ -51,6 +51,7 @@ static const ControlInfoMap RPiControls = {
{ &controls::Brightness, ControlInfo(-1.0f, 1.0f) },
{ &controls::Contrast, ControlInfo(0.0f, 32.0f) },
{ &controls::Saturation, ControlInfo(0.0f, 32.0f) },
+ { &controls::FrameDurationLimits, ControlInfo(1.0e3f, 1.0e9f) }
};
} /* namespace libcamera */
@@ -11,6 +11,7 @@
#include <map>
#include <string.h>
+#include "libcamera/internal/utils.h"
#include "libcamera/internal/v4l2_videodevice.h"
#include "cam_helper.hpp"
@@ -34,8 +35,10 @@ CamHelper *CamHelper::Create(std::string const &cam_name)
return nullptr;
}
-CamHelper::CamHelper(MdParser *parser)
- : parser_(parser), initialized_(false)
+CamHelper::CamHelper(MdParser *parser, unsigned int maxFrameLength,
+ unsigned int frameIntegrationDiff)
+ : parser_(parser), initialized_(false), maxFrameLength_(maxFrameLength),
+ frameIntegrationDiff_(frameIntegrationDiff)
{
}
@@ -56,6 +59,37 @@ double CamHelper::Exposure(uint32_t exposure_lines) const
return exposure_lines * mode_.line_length / 1000.0;
}
+uint32_t CamHelper::GetVBlanking(double &exposure, double minFrameDuration,
+ double maxFrameDuration) const
+{
+ uint32_t frameLengthMin, frameLengthMax, vblank;
+ uint32_t exposureLines = ExposureLines(exposure);
+
+ assert(initialized_);
+ using libcamera::utils::clamp;
+ /*
+ * Clip frame length by the frame duration range and the maximum allowable
+ * value in the sensor, given by maxFrameLength_.
+ */
+ frameLengthMin = clamp<uint32_t>(1e3 * minFrameDuration / mode_.line_length,
+ mode_.height, maxFrameLength_);
+ frameLengthMax = clamp<uint32_t>(1e3 * maxFrameDuration / mode_.line_length,
+ mode_.height, maxFrameLength_);
+ /*
+ * Limit the exposure to the maximum frame duration requested, and
+ * re-calculate if it has been clipped.
+ */
+ exposureLines = std::min(frameLengthMax - frameIntegrationDiff_, exposureLines);
+ exposure = Exposure(exposureLines);
+
+ /* Limit the vblank to the range allowed by the frame length limits. */
+ vblank = std::max<uint32_t>(exposureLines + frameIntegrationDiff_, mode_.height);
+ vblank = clamp(vblank - mode_.height,
+ frameLengthMin - mode_.height, frameLengthMax - mode_.height);
+
+ return vblank;
+}
+
void CamHelper::SetCameraMode(const CameraMode &mode)
{
mode_ = mode;
@@ -68,12 +68,15 @@ class CamHelper
{
public:
static CamHelper *Create(std::string const &cam_name);
- CamHelper(MdParser *parser);
+ CamHelper(MdParser *parser, unsigned int maxFrameLength,
+ unsigned int frameIntegrationDiff);
virtual ~CamHelper();
void SetCameraMode(const CameraMode &mode);
MdParser &Parser() const { return *parser_; }
uint32_t ExposureLines(double exposure_us) const;
double Exposure(uint32_t exposure_lines) const; // in us
+ uint32_t GetVBlanking(double &exposure_us, double minFrameDuration,
+ double maxFrameDuration) const;
virtual uint32_t GainCode(double gain) const = 0;
virtual double Gain(uint32_t gain_code) const = 0;
virtual void GetDelays(int &exposure_delay, int &gain_delay) const;
@@ -83,10 +86,20 @@ public:
virtual unsigned int MistrustFramesStartup() const;
virtual unsigned int MistrustFramesModeSwitch() const;
virtual CamTransform GetOrientation() const;
+
protected:
MdParser *parser_;
CameraMode mode_;
+
+private:
bool initialized_;
+ /* Largest possible frame length, in units of lines. */
+ unsigned int maxFrameLength_;
+ /*
+ * Smallest difference between the frame length and integration time,
+ * in units of lines.
+ */
+ unsigned int frameIntegrationDiff_;
};
// This is for registering camera helpers with the system, so that the
@@ -50,13 +50,22 @@ public:
unsigned int MistrustFramesModeSwitch() const override;
bool SensorEmbeddedDataPresent() const override;
CamTransform GetOrientation() const override;
+
+private:
+ /*
+ * Smallest difference between the frame length and integration time,
+ * in units of lines.
+ */
+ static constexpr int frameIntegrationDiff = 4;
+ /* Largest possible frame length, in units of lines. */
+ static constexpr int maxFrameLength = 0xffff;
};
CamHelperImx219::CamHelperImx219()
#if ENABLE_EMBEDDED_DATA
- : CamHelper(new MdParserImx219())
+ : CamHelper(new MdParserImx219(), maxFrameLength, frameIntegrationDiff)
#else
- : CamHelper(new MdParserRPi())
+ : CamHelper(new MdParserRPi(), maxFrameLength, frameIntegrationDiff)
#endif
{
}
@@ -39,10 +39,19 @@ public:
double Gain(uint32_t gain_code) const override;
bool SensorEmbeddedDataPresent() const override;
CamTransform GetOrientation() const override;
+
+private:
+ /*
+ * Smallest difference between the frame length and integration time,
+ * in units of lines.
+ */
+ static constexpr int frameIntegrationDiff = 22;
+ /* Largest possible frame length, in units of lines. */
+ static constexpr int maxFrameLength = 0xffdc;
};
CamHelperImx477::CamHelperImx477()
- : CamHelper(new MdParserImx477())
+ : CamHelper(new MdParserImx477(), maxFrameLength, frameIntegrationDiff)
{
}
@@ -22,6 +22,15 @@ public:
unsigned int HideFramesModeSwitch() const override;
unsigned int MistrustFramesStartup() const override;
unsigned int MistrustFramesModeSwitch() const override;
+
+private:
+ /*
+ * Smallest difference between the frame length and integration time,
+ * in units of lines.
+ */
+ static constexpr int frameIntegrationDiff = 4;
+ /* Largest possible frame length, in units of lines. */
+ static constexpr int maxFrameLength = 0xffff;
};
/*
@@ -30,7 +39,7 @@ public:
*/
CamHelperOv5647::CamHelperOv5647()
- : CamHelper(new MdParserRPi())
+ : CamHelper(new MdParserRPi(), maxFrameLength, frameIntegrationDiff)
{
}
@@ -54,6 +54,8 @@ namespace libcamera {
/* Configure the sensor with these values initially. */
#define DEFAULT_ANALOGUE_GAIN 1.0
#define DEFAULT_EXPOSURE_TIME 20000
+#define DEFAULT_MIN_FRAME_DURATION (1e6 / 30.0)
+#define DEFAULT_MAX_FRAME_DURATION (1e6 / 0.01)
LOG_DEFINE_CATEGORY(IPARPI)
@@ -137,6 +139,9 @@ private:
/* LS table allocation passed in from the pipeline handler. */
uint32_t lsTableHandle_;
void *lsTable_;
+
+ /* Frame duration (1/fps) given in microseconds. */
+ float minFrameDuration_, maxFrameDuration_;
};
int IPARPi::init(const IPASettings &settings)
@@ -253,13 +258,20 @@ void IPARPi::configure(const CameraSensorInfo &sensorInfo,
controller_.Initialise();
controllerInit_ = true;
- /* Calculate initial values for gain and exposure. */
+ /* Calculate initial values for gain, vblank, and exposure */
+ minFrameDuration_ = DEFAULT_MIN_FRAME_DURATION;
+ maxFrameDuration_ = DEFAULT_MAX_FRAME_DURATION;
+
+ double exposure = DEFAULT_EXPOSURE_TIME;
+ int32_t vblank = helper_->GetVBlanking(exposure, minFrameDuration_,
+ maxFrameDuration_);
+ int32_t exposure_lines = helper_->ExposureLines(exposure);
int32_t gain_code = helper_->GainCode(DEFAULT_ANALOGUE_GAIN);
- int32_t exposure_lines = helper_->ExposureLines(DEFAULT_EXPOSURE_TIME);
ControlList ctrls(unicam_ctrls_);
- ctrls.set(V4L2_CID_ANALOGUE_GAIN, gain_code);
+ ctrls.set(V4L2_CID_VBLANK, vblank);
ctrls.set(V4L2_CID_EXPOSURE, exposure_lines);
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, gain_code);
IPAOperationData op;
op.operation = RPI_IPA_ACTION_V4L2_SET_STAGGERED;
@@ -401,6 +413,8 @@ void IPARPi::reportMetadata()
static_cast<int32_t>(blackLevelStatus->black_level_g),
static_cast<int32_t>(blackLevelStatus->black_level_g),
static_cast<int32_t>(blackLevelStatus->black_level_b) });
+
+ libcameraMetadata_.set(controls::FrameDurationLimits, { minFrameDuration_, maxFrameDuration_ });
}
/*
@@ -631,6 +645,15 @@ void IPARPi::queueRequest(const ControlList &controls)
break;
}
+ case controls::FRAME_DURATION_LIMITS: {
+ auto frameDurations = ctrl.second.get<Span<const float>>();
+
+ /* This will be applied once AGC recalculations occur. */
+ minFrameDuration_ = frameDurations[0];
+ maxFrameDuration_ = frameDurations[1];
+ break;
+ }
+
default:
LOG(IPARPI, Warning)
<< "Ctrl " << controls::controls.at(ctrl.first)->name()
@@ -796,7 +819,12 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus)
op.operation = RPI_IPA_ACTION_V4L2_SET_STAGGERED;
int32_t gain_code = helper_->GainCode(agcStatus->analogue_gain);
- int32_t exposure_lines = helper_->ExposureLines(agcStatus->shutter_time);
+
+ /* GetVBlanking might clip exposure time to the fps limits. */
+ double exposure = agcStatus->shutter_time;
+ int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_,
+ maxFrameDuration_);
+ int32_t exposure_lines = helper_->ExposureLines(exposure);
if (unicam_ctrls_.find(V4L2_CID_ANALOGUE_GAIN) == unicam_ctrls_.end()) {
LOG(IPARPI, Error) << "Can't find analogue gain control";
@@ -808,14 +836,20 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus)
return;
}
- LOG(IPARPI, Debug) << "Applying AGC Exposure: " << agcStatus->shutter_time
- << " (Shutter lines: " << exposure_lines << ") Gain: "
+ LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure
+ << " (Shutter lines: " << exposure_lines << ", AGC requested "
+ << agcStatus->shutter_time << ") Gain: "
<< agcStatus->analogue_gain << " (Gain Code: "
<< gain_code << ")";
ControlList ctrls(unicam_ctrls_);
- ctrls.set(V4L2_CID_ANALOGUE_GAIN, gain_code);
+ /*
+ * VBLANK must be set before EXPOSURE as the former will adjust the
+ * limits of the latter control.
+ */
+ ctrls.set(V4L2_CID_VBLANK, vblanking);
ctrls.set(V4L2_CID_EXPOSURE, exposure_lines);
+ ctrls.set(V4L2_CID_ANALOGUE_GAIN, gain_code);
op.controls.push_back(ctrls);
queueFrameAction.emit(0, op);
}
@@ -1161,7 +1161,9 @@ void RPiCameraData::queueFrameAction(unsigned int frame, const IPAOperationData
if (!staggeredCtrl_) {
staggeredCtrl_.init(unicam_[Unicam::Image].dev(),
{ { V4L2_CID_ANALOGUE_GAIN, action.data[0] },
+ { V4L2_CID_VBLANK, action.data[1] },
{ V4L2_CID_EXPOSURE, action.data[1] } });
+
sensorMetadata_ = action.data[2];
}
Add support for setting V4L2_CID_VBLANK appropriately when setting V4L2_CID_EXPOSURE. This will allow adaptive framerates during viewfinder use cases (e.g. when the exposure time goes above 33ms, we can reduce the framerate to lower than 30fps). The minimum and maximum frame durations are provided via libcamera controls, and will prioritise exposure time limits over any AGC request. V4L2_CID_VBLANK is controlled through the staggered writer, just like the exposure and gain controls. Signed-off-by: Naushir Patuck <naush@raspberrypi.com> --- include/libcamera/ipa/raspberrypi.h | 1 + src/ipa/raspberrypi/cam_helper.cpp | 38 ++++++++++++++- src/ipa/raspberrypi/cam_helper.hpp | 15 +++++- src/ipa/raspberrypi/cam_helper_imx219.cpp | 13 ++++- src/ipa/raspberrypi/cam_helper_imx477.cpp | 11 ++++- src/ipa/raspberrypi/cam_helper_ov5647.cpp | 11 ++++- src/ipa/raspberrypi/raspberrypi.cpp | 48 ++++++++++++++++--- .../pipeline/raspberrypi/raspberrypi.cpp | 2 + 8 files changed, 125 insertions(+), 14 deletions(-)