diff options
-rw-r--r-- | src/ipa/raspberrypi/cam_helper.cpp | 48 | ||||
-rw-r--r-- | src/ipa/raspberrypi/cam_helper.h | 7 | ||||
-rw-r--r-- | src/ipa/raspberrypi/cam_helper_imx477.cpp | 24 | ||||
-rw-r--r-- | src/ipa/raspberrypi/cam_helper_imx519.cpp | 24 | ||||
-rw-r--r-- | 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 bc3f66a5..afbc03d3 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 6cd1dd39..b3f8c980 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 76a82cc5..19a5e471 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 9dff1eeb..d2eb1719 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 5976db05..b74f1ecf 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); } @@ -1110,7 +1110,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; @@ -1156,7 +1156,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. @@ -1177,11 +1177,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")); @@ -1199,10 +1199,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 " @@ -1210,14 +1211,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) |