Message ID | 20221006131744.5179-10-naush@raspberrypi.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Naush, Thank you for the patch. On Thu, Oct 06, 2022 at 02:17:43PM +0100, Naushir Patuck via libcamera-devel wrote: > Rename CamHelper::getVBlanking to CamHelper::getBlanking, and update the > calculations in that function to return both horizontal and vertical blanking > values for a given exposure time and frame duration limits. The calculations > are setup such that vertical blanking is extended to the maximum allowable > value, and any remainder gets put into horizontal blanking. > > The calculated horizontal blanking value is now returned to the pipeline handler > to pass into DelayedControls to program into the sensor. > > Update the IPA to now specify the maximum frame duration from the maximum > horizontal + vertical blanking values provided by the sensor mode. Additionally, > the IPA now uses the frame specific horizontal blanking value (as returned by > DelayedControls) in all instances. > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com> > Tested-by: Dave Stevenson <dave.stevenson@raspberrypi.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > src/ipa/raspberrypi/cam_helper.cpp | 48 +++++++++++++++++------ > src/ipa/raspberrypi/cam_helper.h | 7 ++-- > src/ipa/raspberrypi/cam_helper_imx477.cpp | 24 +++++++----- > src/ipa/raspberrypi/cam_helper_imx519.cpp | 24 +++++++----- > src/ipa/raspberrypi/raspberrypi.cpp | 43 ++++++++++++-------- > 5 files changed, 95 insertions(+), 51 deletions(-) > > diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp > index bc3f66a51254..afbc03d36b02 100644 > --- a/src/ipa/raspberrypi/cam_helper.cpp > +++ b/src/ipa/raspberrypi/cam_helper.cpp > @@ -7,6 +7,7 @@ > > #include <linux/videodev2.h> > > +#include <limits> > #include <map> > #include <string.h> > > @@ -70,31 +71,56 @@ Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) > return exposureLines * lineLength; > } > > -uint32_t CamHelper::getVBlanking(Duration &exposure, > - Duration minFrameDuration, > - Duration maxFrameDuration) const > +std::pair<uint32_t, uint32_t> CamHelper::getBlanking(Duration &exposure, > + Duration minFrameDuration, > + Duration maxFrameDuration) const > { > - uint32_t frameLengthMin, frameLengthMax, vblank; > - uint32_t exposureLines = CamHelper::exposureLines(exposure, mode_.minLineLength); > + uint32_t frameLengthMin, frameLengthMax, vblank, hblank; > + Duration lineLength = mode_.minLineLength; > > /* > * minFrameDuration and maxFrameDuration are clamped by the caller > * based on the limits for the active sensor mode. > + * > + * frameLengthMax gets calculated on the smallest line length as we do > + * not want to extend that unless absolutely necessary. > */ > frameLengthMin = minFrameDuration / mode_.minLineLength; > frameLengthMax = maxFrameDuration / mode_.minLineLength; > > + /* > + * Watch out for (exposureLines + frameIntegrationDiff_) overflowing a > + * uint32_t in the std::clamp() below when the exposure time is > + * extremely (extremely!) long - as happens when the IPA calculates the > + * maximum possible exposure time. > + */ > + uint32_t exposureLines = std::min(CamHelper::exposureLines(exposure, lineLength), > + std::numeric_limits<uint32_t>::max() - frameIntegrationDiff_); > + uint32_t frameLengthLines = std::clamp(exposureLines + frameIntegrationDiff_, > + frameLengthMin, frameLengthMax); > + > + /* > + * If our frame length lines is above the maximum allowed, see if we can > + * extend the line length to accommodate the requested frame length. > + */ > + if (frameLengthLines > mode_.maxFrameLength) { > + Duration lineLengthAdjusted = lineLength * frameLengthLines / mode_.maxFrameLength; > + lineLength = std::min(mode_.maxLineLength, lineLengthAdjusted); > + frameLengthLines = mode_.maxFrameLength; > + } > + > + hblank = lineLengthToHblank(lineLength); > + vblank = frameLengthLines - mode_.height; > + > /* > * Limit the exposure to the maximum frame duration requested, and > * re-calculate if it has been clipped. > */ > - exposureLines = std::min(frameLengthMax - frameIntegrationDiff_, exposureLines); > - exposure = CamHelper::exposure(exposureLines, mode_.minLineLength); > + exposureLines = std::min(frameLengthLines - frameIntegrationDiff_, > + CamHelper::exposureLines(exposure, lineLength)); > + exposure = CamHelper::exposure(exposureLines, lineLength); > > - /* Limit the vblank to the range allowed by the frame length limits. */ > - vblank = std::clamp(exposureLines + frameIntegrationDiff_, > - frameLengthMin, frameLengthMax) - mode_.height; > - return vblank; > + return { vblank, hblank }; > } > > Duration CamHelper::hblankToLineLength(uint32_t hblank) const > diff --git a/src/ipa/raspberrypi/cam_helper.h b/src/ipa/raspberrypi/cam_helper.h > index 6cd1dd397b91..b3f8c9803094 100644 > --- a/src/ipa/raspberrypi/cam_helper.h > +++ b/src/ipa/raspberrypi/cam_helper.h > @@ -8,6 +8,7 @@ > > #include <memory> > #include <string> > +#include <utility> > > #include <libcamera/base/span.h> > #include <libcamera/base/utils.h> > @@ -82,9 +83,9 @@ public: > const libcamera::utils::Duration lineLength) const; > virtual libcamera::utils::Duration exposure(uint32_t exposureLines, > const libcamera::utils::Duration lineLength) const; > - virtual uint32_t getVBlanking(libcamera::utils::Duration &exposure, > - libcamera::utils::Duration minFrameDuration, > - libcamera::utils::Duration maxFrameDuration) const; > + virtual std::pair<uint32_t, uint32_t> getBlanking(libcamera::utils::Duration &exposure, > + libcamera::utils::Duration minFrameDuration, > + libcamera::utils::Duration maxFrameDuration) const; > libcamera::utils::Duration hblankToLineLength(uint32_t hblank) const; > uint32_t lineLengthToHblank(const libcamera::utils::Duration &duration) const; > libcamera::utils::Duration lineLengthPckToDuration(uint32_t lineLengthPck) const; > diff --git a/src/ipa/raspberrypi/cam_helper_imx477.cpp b/src/ipa/raspberrypi/cam_helper_imx477.cpp > index 76a82cc51378..19a5e471c27e 100644 > --- a/src/ipa/raspberrypi/cam_helper_imx477.cpp > +++ b/src/ipa/raspberrypi/cam_helper_imx477.cpp > @@ -46,8 +46,8 @@ public: > uint32_t gainCode(double gain) const override; > double gain(uint32_t gainCode) const override; > void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override; > - uint32_t getVBlanking(Duration &exposure, Duration minFrameDuration, > - Duration maxFrameDuration) const override; > + std::pair<uint32_t, uint32_t> getBlanking(Duration &exposure, Duration minFrameDuration, > + Duration maxFrameDuration) const override; > void getDelays(int &exposureDelay, int &gainDelay, > int &vblankDelay, int &hblankDelay) const override; > bool sensorEmbeddedDataPresent() const override; > @@ -118,15 +118,19 @@ void CamHelperImx477::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m > } > } > > -uint32_t CamHelperImx477::getVBlanking(Duration &exposure, > - Duration minFrameDuration, > - Duration maxFrameDuration) const > +std::pair<uint32_t, uint32_t> CamHelperImx477::getBlanking(Duration &exposure, > + Duration minFrameDuration, > + Duration maxFrameDuration) const > { > uint32_t frameLength, exposureLines; > unsigned int shift = 0; > > - frameLength = mode_.height + CamHelper::getVBlanking(exposure, minFrameDuration, > - maxFrameDuration); > + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, > + maxFrameDuration); > + > + frameLength = mode_.height + vblank; > + Duration lineLength = hblankToLineLength(hblank); > + > /* > * Check if the frame length calculated needs to be setup for long > * exposure mode. This will require us to use a long exposure scale > @@ -144,12 +148,12 @@ uint32_t CamHelperImx477::getVBlanking(Duration &exposure, > if (shift) { > /* Account for any rounding in the scaled frame length value. */ > frameLength <<= shift; > - exposureLines = CamHelperImx477::exposureLines(exposure, mode_.minLineLength); > + exposureLines = CamHelperImx477::exposureLines(exposure, lineLength); > exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); > - exposure = CamHelperImx477::exposure(exposureLines, mode_.minLineLength); > + exposure = CamHelperImx477::exposure(exposureLines, lineLength); > } > > - return frameLength - mode_.height; > + return { frameLength - mode_.height, hblank }; > } > > void CamHelperImx477::getDelays(int &exposureDelay, int &gainDelay, > diff --git a/src/ipa/raspberrypi/cam_helper_imx519.cpp b/src/ipa/raspberrypi/cam_helper_imx519.cpp > index 9dff1eeb899f..d2eb171912da 100644 > --- a/src/ipa/raspberrypi/cam_helper_imx519.cpp > +++ b/src/ipa/raspberrypi/cam_helper_imx519.cpp > @@ -46,8 +46,8 @@ public: > uint32_t gainCode(double gain) const override; > double gain(uint32_t gainCode) const override; > void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override; > - uint32_t getVBlanking(Duration &exposure, Duration minFrameDuration, > - Duration maxFrameDuration) const override; > + std::pair<uint32_t, uint32_t> getBlanking(Duration &exposure, Duration minFrameDuration, > + Duration maxFrameDuration) const override; > void getDelays(int &exposureDelay, int &gainDelay, > int &vblankDelay, int &hblankDelay) const override; > bool sensorEmbeddedDataPresent() const override; > @@ -118,15 +118,19 @@ void CamHelperImx519::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m > } > } > > -uint32_t CamHelperImx519::getVBlanking(Duration &exposure, > - Duration minFrameDuration, > - Duration maxFrameDuration) const > +std::pair<uint32_t, uint32_t> CamHelperImx519::getBlanking(Duration &exposure, > + Duration minFrameDuration, > + Duration maxFrameDuration) const > { > uint32_t frameLength, exposureLines; > unsigned int shift = 0; > > - frameLength = mode_.height + CamHelper::getVBlanking(exposure, minFrameDuration, > - maxFrameDuration); > + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, > + maxFrameDuration); > + > + frameLength = mode_.height + vblank; > + Duration lineLength = hblankToLineLength(hblank); > + > /* > * Check if the frame length calculated needs to be setup for long > * exposure mode. This will require us to use a long exposure scale > @@ -144,12 +148,12 @@ uint32_t CamHelperImx519::getVBlanking(Duration &exposure, > if (shift) { > /* Account for any rounding in the scaled frame length value. */ > frameLength <<= shift; > - exposureLines = CamHelperImx519::exposureLines(exposure, mode_.minLineLength); > + exposureLines = CamHelperImx519::exposureLines(exposure, lineLength); > exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); > - exposure = CamHelperImx519::exposure(exposureLines, mode_.minLineLength); > + exposure = CamHelperImx519::exposure(exposureLines, lineLength); > } > > - return frameLength - mode_.height; > + return { frameLength - mode_.height, hblank }; > } > > void CamHelperImx519::getDelays(int &exposureDelay, int &gainDelay, > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp > index c8e15f2b04d1..497a83939ae6 100644 > --- a/src/ipa/raspberrypi/raspberrypi.cpp > +++ b/src/ipa/raspberrypi/raspberrypi.cpp > @@ -315,7 +315,7 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig) > } > > startConfig->dropFrameCount = dropFrameCount_; > - const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.minLineLength; > + const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; > startConfig->maxSensorFrameLengthMs = maxSensorFrameDuration.get<std::milli>(); > > firstStart_ = false; > @@ -462,7 +462,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, > */ > ControlInfoMap::Map ctrlMap = ipaControls; > const Duration minSensorFrameDuration = mode_.minFrameLength * mode_.minLineLength; > - const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.minLineLength; > + const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; > ctrlMap[&controls::FrameDurationLimits] = > ControlInfo(static_cast<int64_t>(minSensorFrameDuration.get<std::micro>()), > static_cast<int64_t>(maxSensorFrameDuration.get<std::micro>())); > @@ -475,7 +475,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, > * will limit the maximum control value based on the current VBLANK value. > */ > Duration maxShutter = Duration::max(); > - helper_->getVBlanking(maxShutter, minSensorFrameDuration, maxSensorFrameDuration); > + helper_->getBlanking(maxShutter, minSensorFrameDuration, maxSensorFrameDuration); > const uint32_t exposureMin = sensorCtrls_.at(V4L2_CID_EXPOSURE).min().get<int32_t>(); > > ctrlMap[&controls::ExposureTime] = > @@ -552,7 +552,7 @@ void IPARPi::reportMetadata() > deviceStatus->shutterSpeed.get<std::micro>()); > libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain); > libcameraMetadata_.set(controls::FrameDuration, > - helper_->exposure(deviceStatus->frameLength, mode_.minLineLength).get<std::micro>()); > + helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get<std::micro>()); > if (deviceStatus->sensorTemperature) > libcameraMetadata_.set(controls::SensorTemperature, *deviceStatus->sensorTemperature); > } > @@ -1111,7 +1111,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls) > int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get<int32_t>(); > > deviceStatus.lineLength = helper_->hblankToLineLength(hblank); > - deviceStatus.shutterSpeed = helper_->exposure(exposureLines, mode_.minLineLength); > + deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength); > deviceStatus.analogueGain = helper_->gain(gainCode); > deviceStatus.frameLength = mode_.height + vblank; > > @@ -1157,7 +1157,7 @@ void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls) > void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration) > { > const Duration minSensorFrameDuration = mode_.minFrameLength * mode_.minLineLength; > - const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.minLineLength; > + const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; > > /* > * This will only be applied once AGC recalculations occur. > @@ -1178,11 +1178,11 @@ void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDur > > /* > * Calculate the maximum exposure time possible for the AGC to use. > - * getVBlanking() will update maxShutter with the largest exposure > + * getBlanking() will update maxShutter with the largest exposure > * value possible. > */ > Duration maxShutter = Duration::max(); > - helper_->getVBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); > + helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); > > RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( > controller_.getAlgorithm("agc")); > @@ -1200,10 +1200,11 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) > */ > gainCode = std::min<int32_t>(gainCode, maxSensorGainCode_); > > - /* getVBlanking might clip exposure time to the fps limits. */ > + /* getBlanking might clip exposure time to the fps limits. */ > Duration exposure = agcStatus->shutterTime; > - int32_t vblanking = helper_->getVBlanking(exposure, minFrameDuration_, maxFrameDuration_); > - int32_t exposureLines = helper_->exposureLines(exposure, mode_.minLineLength); > + auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_); > + int32_t exposureLines = helper_->exposureLines(exposure, > + helper_->hblankToLineLength(hblank)); > > LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure > << " (Shutter lines: " << exposureLines << ", AGC requested " > @@ -1211,14 +1212,22 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) > << agcStatus->analogueGain << " (Gain Code: " > << gainCode << ")"; > > - /* > - * Due to the behavior of V4L2, the current value of VBLANK could clip the > - * exposure time without us knowing. The next time though this function should > - * clip exposure correctly. > - */ > - ctrls.set(V4L2_CID_VBLANK, vblanking); > + ctrls.set(V4L2_CID_VBLANK, static_cast<int32_t>(vblank)); > ctrls.set(V4L2_CID_EXPOSURE, exposureLines); > ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode); > + > + /* > + * At present, there is no way of knowing if a control is read-only. > + * As a workaround, assume that if the minimum and maximum values of > + * the V4L2_CID_HBLANK control are the same, it implies the control > + * is read-only. This seems to be the case for all the cameras our IPA > + * works with. > + * > + * \todo The control API ought to have a flag to specify if a control > + * is read-only which could be used below. > + */ > + if (mode_.minLineLength != mode_.maxLineLength) > + ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank)); > } > > void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)
diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp index bc3f66a51254..afbc03d36b02 100644 --- a/src/ipa/raspberrypi/cam_helper.cpp +++ b/src/ipa/raspberrypi/cam_helper.cpp @@ -7,6 +7,7 @@ #include <linux/videodev2.h> +#include <limits> #include <map> #include <string.h> @@ -70,31 +71,56 @@ Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) return exposureLines * lineLength; } -uint32_t CamHelper::getVBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const +std::pair<uint32_t, uint32_t> CamHelper::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const { - uint32_t frameLengthMin, frameLengthMax, vblank; - uint32_t exposureLines = CamHelper::exposureLines(exposure, mode_.minLineLength); + uint32_t frameLengthMin, frameLengthMax, vblank, hblank; + Duration lineLength = mode_.minLineLength; /* * minFrameDuration and maxFrameDuration are clamped by the caller * based on the limits for the active sensor mode. + * + * frameLengthMax gets calculated on the smallest line length as we do + * not want to extend that unless absolutely necessary. */ frameLengthMin = minFrameDuration / mode_.minLineLength; frameLengthMax = maxFrameDuration / mode_.minLineLength; + /* + * Watch out for (exposureLines + frameIntegrationDiff_) overflowing a + * uint32_t in the std::clamp() below when the exposure time is + * extremely (extremely!) long - as happens when the IPA calculates the + * maximum possible exposure time. + */ + uint32_t exposureLines = std::min(CamHelper::exposureLines(exposure, lineLength), + std::numeric_limits<uint32_t>::max() - frameIntegrationDiff_); + uint32_t frameLengthLines = std::clamp(exposureLines + frameIntegrationDiff_, + frameLengthMin, frameLengthMax); + + /* + * If our frame length lines is above the maximum allowed, see if we can + * extend the line length to accommodate the requested frame length. + */ + if (frameLengthLines > mode_.maxFrameLength) { + Duration lineLengthAdjusted = lineLength * frameLengthLines / mode_.maxFrameLength; + lineLength = std::min(mode_.maxLineLength, lineLengthAdjusted); + frameLengthLines = mode_.maxFrameLength; + } + + hblank = lineLengthToHblank(lineLength); + vblank = frameLengthLines - mode_.height; + /* * Limit the exposure to the maximum frame duration requested, and * re-calculate if it has been clipped. */ - exposureLines = std::min(frameLengthMax - frameIntegrationDiff_, exposureLines); - exposure = CamHelper::exposure(exposureLines, mode_.minLineLength); + exposureLines = std::min(frameLengthLines - frameIntegrationDiff_, + CamHelper::exposureLines(exposure, lineLength)); + exposure = CamHelper::exposure(exposureLines, lineLength); - /* Limit the vblank to the range allowed by the frame length limits. */ - vblank = std::clamp(exposureLines + frameIntegrationDiff_, - frameLengthMin, frameLengthMax) - mode_.height; - return vblank; + return { vblank, hblank }; } Duration CamHelper::hblankToLineLength(uint32_t hblank) const diff --git a/src/ipa/raspberrypi/cam_helper.h b/src/ipa/raspberrypi/cam_helper.h index 6cd1dd397b91..b3f8c9803094 100644 --- a/src/ipa/raspberrypi/cam_helper.h +++ b/src/ipa/raspberrypi/cam_helper.h @@ -8,6 +8,7 @@ #include <memory> #include <string> +#include <utility> #include <libcamera/base/span.h> #include <libcamera/base/utils.h> @@ -82,9 +83,9 @@ public: const libcamera::utils::Duration lineLength) const; virtual libcamera::utils::Duration exposure(uint32_t exposureLines, const libcamera::utils::Duration lineLength) const; - virtual uint32_t getVBlanking(libcamera::utils::Duration &exposure, - libcamera::utils::Duration minFrameDuration, - libcamera::utils::Duration maxFrameDuration) const; + virtual std::pair<uint32_t, uint32_t> getBlanking(libcamera::utils::Duration &exposure, + libcamera::utils::Duration minFrameDuration, + libcamera::utils::Duration maxFrameDuration) const; libcamera::utils::Duration hblankToLineLength(uint32_t hblank) const; uint32_t lineLengthToHblank(const libcamera::utils::Duration &duration) const; libcamera::utils::Duration lineLengthPckToDuration(uint32_t lineLengthPck) const; diff --git a/src/ipa/raspberrypi/cam_helper_imx477.cpp b/src/ipa/raspberrypi/cam_helper_imx477.cpp index 76a82cc51378..19a5e471c27e 100644 --- a/src/ipa/raspberrypi/cam_helper_imx477.cpp +++ b/src/ipa/raspberrypi/cam_helper_imx477.cpp @@ -46,8 +46,8 @@ public: uint32_t gainCode(double gain) const override; double gain(uint32_t gainCode) const override; void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override; - uint32_t getVBlanking(Duration &exposure, Duration minFrameDuration, - Duration maxFrameDuration) const override; + std::pair<uint32_t, uint32_t> getBlanking(Duration &exposure, Duration minFrameDuration, + Duration maxFrameDuration) const override; void getDelays(int &exposureDelay, int &gainDelay, int &vblankDelay, int &hblankDelay) const override; bool sensorEmbeddedDataPresent() const override; @@ -118,15 +118,19 @@ void CamHelperImx477::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m } } -uint32_t CamHelperImx477::getVBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const +std::pair<uint32_t, uint32_t> CamHelperImx477::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const { uint32_t frameLength, exposureLines; unsigned int shift = 0; - frameLength = mode_.height + CamHelper::getVBlanking(exposure, minFrameDuration, - maxFrameDuration); + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, + maxFrameDuration); + + frameLength = mode_.height + vblank; + Duration lineLength = hblankToLineLength(hblank); + /* * Check if the frame length calculated needs to be setup for long * exposure mode. This will require us to use a long exposure scale @@ -144,12 +148,12 @@ uint32_t CamHelperImx477::getVBlanking(Duration &exposure, if (shift) { /* Account for any rounding in the scaled frame length value. */ frameLength <<= shift; - exposureLines = CamHelperImx477::exposureLines(exposure, mode_.minLineLength); + exposureLines = CamHelperImx477::exposureLines(exposure, lineLength); exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); - exposure = CamHelperImx477::exposure(exposureLines, mode_.minLineLength); + exposure = CamHelperImx477::exposure(exposureLines, lineLength); } - return frameLength - mode_.height; + return { frameLength - mode_.height, hblank }; } void CamHelperImx477::getDelays(int &exposureDelay, int &gainDelay, diff --git a/src/ipa/raspberrypi/cam_helper_imx519.cpp b/src/ipa/raspberrypi/cam_helper_imx519.cpp index 9dff1eeb899f..d2eb171912da 100644 --- a/src/ipa/raspberrypi/cam_helper_imx519.cpp +++ b/src/ipa/raspberrypi/cam_helper_imx519.cpp @@ -46,8 +46,8 @@ public: uint32_t gainCode(double gain) const override; double gain(uint32_t gainCode) const override; void prepare(libcamera::Span<const uint8_t> buffer, Metadata &metadata) override; - uint32_t getVBlanking(Duration &exposure, Duration minFrameDuration, - Duration maxFrameDuration) const override; + std::pair<uint32_t, uint32_t> getBlanking(Duration &exposure, Duration minFrameDuration, + Duration maxFrameDuration) const override; void getDelays(int &exposureDelay, int &gainDelay, int &vblankDelay, int &hblankDelay) const override; bool sensorEmbeddedDataPresent() const override; @@ -118,15 +118,19 @@ void CamHelperImx519::prepare(libcamera::Span<const uint8_t> buffer, Metadata &m } } -uint32_t CamHelperImx519::getVBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const +std::pair<uint32_t, uint32_t> CamHelperImx519::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const { uint32_t frameLength, exposureLines; unsigned int shift = 0; - frameLength = mode_.height + CamHelper::getVBlanking(exposure, minFrameDuration, - maxFrameDuration); + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, + maxFrameDuration); + + frameLength = mode_.height + vblank; + Duration lineLength = hblankToLineLength(hblank); + /* * Check if the frame length calculated needs to be setup for long * exposure mode. This will require us to use a long exposure scale @@ -144,12 +148,12 @@ uint32_t CamHelperImx519::getVBlanking(Duration &exposure, if (shift) { /* Account for any rounding in the scaled frame length value. */ frameLength <<= shift; - exposureLines = CamHelperImx519::exposureLines(exposure, mode_.minLineLength); + exposureLines = CamHelperImx519::exposureLines(exposure, lineLength); exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); - exposure = CamHelperImx519::exposure(exposureLines, mode_.minLineLength); + exposure = CamHelperImx519::exposure(exposureLines, lineLength); } - return frameLength - mode_.height; + return { frameLength - mode_.height, hblank }; } void CamHelperImx519::getDelays(int &exposureDelay, int &gainDelay, diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index c8e15f2b04d1..497a83939ae6 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -315,7 +315,7 @@ void IPARPi::start(const ControlList &controls, StartConfig *startConfig) } startConfig->dropFrameCount = dropFrameCount_; - const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.minLineLength; + const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; startConfig->maxSensorFrameLengthMs = maxSensorFrameDuration.get<std::milli>(); firstStart_ = false; @@ -462,7 +462,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, */ ControlInfoMap::Map ctrlMap = ipaControls; const Duration minSensorFrameDuration = mode_.minFrameLength * mode_.minLineLength; - const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.minLineLength; + const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; ctrlMap[&controls::FrameDurationLimits] = ControlInfo(static_cast<int64_t>(minSensorFrameDuration.get<std::micro>()), static_cast<int64_t>(maxSensorFrameDuration.get<std::micro>())); @@ -475,7 +475,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, * will limit the maximum control value based on the current VBLANK value. */ Duration maxShutter = Duration::max(); - helper_->getVBlanking(maxShutter, minSensorFrameDuration, maxSensorFrameDuration); + helper_->getBlanking(maxShutter, minSensorFrameDuration, maxSensorFrameDuration); const uint32_t exposureMin = sensorCtrls_.at(V4L2_CID_EXPOSURE).min().get<int32_t>(); ctrlMap[&controls::ExposureTime] = @@ -552,7 +552,7 @@ void IPARPi::reportMetadata() deviceStatus->shutterSpeed.get<std::micro>()); libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain); libcameraMetadata_.set(controls::FrameDuration, - helper_->exposure(deviceStatus->frameLength, mode_.minLineLength).get<std::micro>()); + helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get<std::micro>()); if (deviceStatus->sensorTemperature) libcameraMetadata_.set(controls::SensorTemperature, *deviceStatus->sensorTemperature); } @@ -1111,7 +1111,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls) int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get<int32_t>(); deviceStatus.lineLength = helper_->hblankToLineLength(hblank); - deviceStatus.shutterSpeed = helper_->exposure(exposureLines, mode_.minLineLength); + deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength); deviceStatus.analogueGain = helper_->gain(gainCode); deviceStatus.frameLength = mode_.height + vblank; @@ -1157,7 +1157,7 @@ void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls) void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration) { const Duration minSensorFrameDuration = mode_.minFrameLength * mode_.minLineLength; - const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.minLineLength; + const Duration maxSensorFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; /* * This will only be applied once AGC recalculations occur. @@ -1178,11 +1178,11 @@ void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDur /* * Calculate the maximum exposure time possible for the AGC to use. - * getVBlanking() will update maxShutter with the largest exposure + * getBlanking() will update maxShutter with the largest exposure * value possible. */ Duration maxShutter = Duration::max(); - helper_->getVBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); + helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( controller_.getAlgorithm("agc")); @@ -1200,10 +1200,11 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) */ gainCode = std::min<int32_t>(gainCode, maxSensorGainCode_); - /* getVBlanking might clip exposure time to the fps limits. */ + /* getBlanking might clip exposure time to the fps limits. */ Duration exposure = agcStatus->shutterTime; - int32_t vblanking = helper_->getVBlanking(exposure, minFrameDuration_, maxFrameDuration_); - int32_t exposureLines = helper_->exposureLines(exposure, mode_.minLineLength); + auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_); + int32_t exposureLines = helper_->exposureLines(exposure, + helper_->hblankToLineLength(hblank)); LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure << " (Shutter lines: " << exposureLines << ", AGC requested " @@ -1211,14 +1212,22 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) << agcStatus->analogueGain << " (Gain Code: " << gainCode << ")"; - /* - * Due to the behavior of V4L2, the current value of VBLANK could clip the - * exposure time without us knowing. The next time though this function should - * clip exposure correctly. - */ - ctrls.set(V4L2_CID_VBLANK, vblanking); + ctrls.set(V4L2_CID_VBLANK, static_cast<int32_t>(vblank)); ctrls.set(V4L2_CID_EXPOSURE, exposureLines); ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode); + + /* + * At present, there is no way of knowing if a control is read-only. + * As a workaround, assume that if the minimum and maximum values of + * the V4L2_CID_HBLANK control are the same, it implies the control + * is read-only. This seems to be the case for all the cameras our IPA + * works with. + * + * \todo The control API ought to have a flag to specify if a control + * is read-only which could be used below. + */ + if (mode_.minLineLength != mode_.maxLineLength) + ctrls.set(V4L2_CID_HBLANK, static_cast<int32_t>(hblank)); } void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls)