Message ID | 20221124025133.17875-5-laurent.pinchart@ideasonboard.com |
---|---|
State | Accepted |
Headers | show |
Series |
|
Related | show |
Hi Laurent On Thu, Nov 24, 2022 at 04:51:28AM +0200, Laurent Pinchart via libcamera-devel wrote: > From: Paul Elder <paul.elder@ideasonboard.com> > > Add support for manual gain and exposure in the rkisp1 IPA. > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Looks good! Reviewed-by: Jacopo Mondi <jacopo@jmondi.org> Thanks j > --- > Changes since v3: > > - Convert exposure and gain V4L2 controls limits to libcamera values > - Convert exposure control from duration to lines in AGC > - Initialize lineDuration in init() > - Small fixes in debug messages > - Drop unnecessary curly braces > - Use std::piecewise_construct > --- > src/ipa/rkisp1/algorithms/agc.cpp | 63 +++++++++++++++++++++++++++---- > src/ipa/rkisp1/algorithms/agc.h | 4 ++ > src/ipa/rkisp1/ipa_context.h | 13 ++++++- > src/ipa/rkisp1/rkisp1.cpp | 25 ++++++++++++ > 4 files changed, 96 insertions(+), 9 deletions(-) > > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp > index 169afe372803..ccbcd4a9583d 100644 > --- a/src/ipa/rkisp1/algorithms/agc.cpp > +++ b/src/ipa/rkisp1/algorithms/agc.cpp > @@ -74,9 +74,14 @@ Agc::Agc() > int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) > { > /* Configure the default exposure and gain. */ > - context.activeState.agc.gain = std::max(context.configuration.sensor.minAnalogueGain, > - kMinAnalogueGain); > - context.activeState.agc.exposure = 10ms / context.configuration.sensor.lineDuration; > + context.activeState.agc.automatic.gain = > + std::max(context.configuration.sensor.minAnalogueGain, > + kMinAnalogueGain); > + context.activeState.agc.automatic.exposure = > + 10ms / context.configuration.sensor.lineDuration; > + context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain; > + context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure; > + context.activeState.agc.autoEnabled = true; > > /* > * According to the RkISP1 documentation: > @@ -108,14 +113,58 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) > return 0; > } > > +/** > + * \copydoc libcamera::ipa::Algorithm::queueRequest > + */ > +void Agc::queueRequest(IPAContext &context, > + [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, > + const ControlList &controls) > +{ > + auto &agc = context.activeState.agc; > + > + const auto &agcEnable = controls.get(controls::AeEnable); > + if (agcEnable && *agcEnable != agc.autoEnabled) { > + agc.autoEnabled = *agcEnable; > + > + LOG(RkISP1Agc, Debug) > + << (agc.autoEnabled ? "Enabling" : "Disabling") << " AGC"; > + } > + > + const auto &exposure = controls.get(controls::ExposureTime); > + if (exposure && !agc.autoEnabled) { > + agc.manual.exposure = *exposure * 1.0us > + / context.configuration.sensor.lineDuration; > + > + LOG(RkISP1Agc, Debug) > + << "Set exposure to " << agc.manual.exposure; > + } > + > + const auto &gain = controls.get(controls::AnalogueGain); > + if (gain && !agc.autoEnabled) { > + agc.manual.gain = *gain; > + > + LOG(RkISP1Agc, Debug) << "Set gain to " << agc.manual.gain; > + } > + > + frameContext.agc.autoEnabled = agc.autoEnabled; > + > + if (!frameContext.agc.autoEnabled) { > + frameContext.agc.exposure = agc.manual.exposure; > + frameContext.agc.gain = agc.manual.gain; > + } > +} > + > /** > * \copydoc libcamera::ipa::Algorithm::prepare > */ > void Agc::prepare(IPAContext &context, const uint32_t frame, > IPAFrameContext &frameContext, rkisp1_params_cfg *params) > { > - frameContext.agc.exposure = context.activeState.agc.exposure; > - frameContext.agc.gain = context.activeState.agc.gain; > + if (frameContext.agc.autoEnabled) { > + frameContext.agc.exposure = context.activeState.agc.automatic.exposure; > + frameContext.agc.gain = context.activeState.agc.automatic.gain; > + } > > if (frame > 0) > return; > @@ -263,8 +312,8 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext, > << stepGain; > > /* Update the estimated exposure and gain. */ > - activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration; > - activeState.agc.gain = stepGain; > + activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; > + activeState.agc.automatic.gain = stepGain; > } > > /** > diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h > index da4d2d4e8359..a228d0c37768 100644 > --- a/src/ipa/rkisp1/algorithms/agc.h > +++ b/src/ipa/rkisp1/algorithms/agc.h > @@ -26,6 +26,10 @@ public: > ~Agc() = default; > > int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; > + void queueRequest(IPAContext &context, > + const uint32_t frame, > + IPAFrameContext &frameContext, > + const ControlList &controls) override; > void prepare(IPAContext &context, const uint32_t frame, > IPAFrameContext &frameContext, > rkisp1_params_cfg *params) override; > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h > index 3e47ac663c58..b9b2065328d6 100644 > --- a/src/ipa/rkisp1/ipa_context.h > +++ b/src/ipa/rkisp1/ipa_context.h > @@ -54,8 +54,16 @@ struct IPASessionConfiguration { > > struct IPAActiveState { > struct { > - uint32_t exposure; > - double gain; > + struct { > + uint32_t exposure; > + double gain; > + } manual; > + struct { > + uint32_t exposure; > + double gain; > + } automatic; > + > + bool autoEnabled; > } agc; > > struct { > @@ -96,6 +104,7 @@ struct IPAFrameContext : public FrameContext { > struct { > uint32_t exposure; > double gain; > + bool autoEnabled; > } agc; > > struct { > diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp > index 6ac29df8ec8d..76e818d2d11e 100644 > --- a/src/ipa/rkisp1/rkisp1.cpp > +++ b/src/ipa/rkisp1/rkisp1.cpp > @@ -158,6 +158,9 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, > return -ENODEV; > } > > + context_.configuration.sensor.lineDuration = sensorInfo.minLineLength > + * 1.0s / sensorInfo.pixelRate; > + > /* Load the tuning data file. */ > File file(settings.configurationFile); > if (!file.open(File::OpenModeFlag::ReadOnly)) { > @@ -377,6 +380,28 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo, > { > ControlInfoMap::Map ctrlMap = rkisp1Controls; > > + /* > + * Compute exposure time limits from the V4L2_CID_EXPOSURE control > + * limits and the line duration. > + */ > + double lineDuration = context_.configuration.sensor.lineDuration.get<std::micro>(); > + const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; > + int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration; > + int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration; > + int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration; > + ctrlMap.emplace(std::piecewise_construct, > + std::forward_as_tuple(&controls::ExposureTime), > + std::forward_as_tuple(minExposure, maxExposure, defExposure)); > + > + /* Compute the analogue gain limits. */ > + const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; > + float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>()); > + float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>()); > + float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>()); > + ctrlMap.emplace(std::piecewise_construct, > + std::forward_as_tuple(&controls::AnalogueGain), > + std::forward_as_tuple(minGain, maxGain, defGain)); > + > /* > * Compute the frame duration limits. > * > -- > Regards, > > Laurent Pinchart >
On Thu, Nov 24, 2022 at 04:51:28AM +0200, Laurent Pinchart wrote: > From: Paul Elder <paul.elder@ideasonboard.com> > > Add support for manual gain and exposure in the rkisp1 IPA. > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> > --- > Changes since v3: > > - Convert exposure and gain V4L2 controls limits to libcamera values > - Convert exposure control from duration to lines in AGC > - Initialize lineDuration in init() > - Small fixes in debug messages > - Drop unnecessary curly braces > - Use std::piecewise_construct > --- > src/ipa/rkisp1/algorithms/agc.cpp | 63 +++++++++++++++++++++++++++---- > src/ipa/rkisp1/algorithms/agc.h | 4 ++ > src/ipa/rkisp1/ipa_context.h | 13 ++++++- > src/ipa/rkisp1/rkisp1.cpp | 25 ++++++++++++ > 4 files changed, 96 insertions(+), 9 deletions(-) > > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp > index 169afe372803..ccbcd4a9583d 100644 > --- a/src/ipa/rkisp1/algorithms/agc.cpp > +++ b/src/ipa/rkisp1/algorithms/agc.cpp > @@ -74,9 +74,14 @@ Agc::Agc() > int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) > { > /* Configure the default exposure and gain. */ > - context.activeState.agc.gain = std::max(context.configuration.sensor.minAnalogueGain, > - kMinAnalogueGain); > - context.activeState.agc.exposure = 10ms / context.configuration.sensor.lineDuration; > + context.activeState.agc.automatic.gain = > + std::max(context.configuration.sensor.minAnalogueGain, > + kMinAnalogueGain); > + context.activeState.agc.automatic.exposure = > + 10ms / context.configuration.sensor.lineDuration; > + context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain; > + context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure; > + context.activeState.agc.autoEnabled = true; > > /* > * According to the RkISP1 documentation: > @@ -108,14 +113,58 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) > return 0; > } > > +/** > + * \copydoc libcamera::ipa::Algorithm::queueRequest > + */ > +void Agc::queueRequest(IPAContext &context, > + [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, > + const ControlList &controls) > +{ > + auto &agc = context.activeState.agc; > + > + const auto &agcEnable = controls.get(controls::AeEnable); > + if (agcEnable && *agcEnable != agc.autoEnabled) { > + agc.autoEnabled = *agcEnable; > + > + LOG(RkISP1Agc, Debug) > + << (agc.autoEnabled ? "Enabling" : "Disabling") << " AGC"; > + } > + > + const auto &exposure = controls.get(controls::ExposureTime); > + if (exposure && !agc.autoEnabled) { > + agc.manual.exposure = *exposure * 1.0us > + / context.configuration.sensor.lineDuration; > + > + LOG(RkISP1Agc, Debug) > + << "Set exposure to " << agc.manual.exposure; > + } > + > + const auto &gain = controls.get(controls::AnalogueGain); > + if (gain && !agc.autoEnabled) { > + agc.manual.gain = *gain; > + > + LOG(RkISP1Agc, Debug) << "Set gain to " << agc.manual.gain; > + } > + > + frameContext.agc.autoEnabled = agc.autoEnabled; > + > + if (!frameContext.agc.autoEnabled) { > + frameContext.agc.exposure = agc.manual.exposure; > + frameContext.agc.gain = agc.manual.gain; > + } > +} > + > /** > * \copydoc libcamera::ipa::Algorithm::prepare > */ > void Agc::prepare(IPAContext &context, const uint32_t frame, > IPAFrameContext &frameContext, rkisp1_params_cfg *params) > { > - frameContext.agc.exposure = context.activeState.agc.exposure; > - frameContext.agc.gain = context.activeState.agc.gain; > + if (frameContext.agc.autoEnabled) { > + frameContext.agc.exposure = context.activeState.agc.automatic.exposure; > + frameContext.agc.gain = context.activeState.agc.automatic.gain; > + } > > if (frame > 0) > return; > @@ -263,8 +312,8 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext, > << stepGain; > > /* Update the estimated exposure and gain. */ > - activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration; > - activeState.agc.gain = stepGain; > + activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; > + activeState.agc.automatic.gain = stepGain; > } > > /** > diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h > index da4d2d4e8359..a228d0c37768 100644 > --- a/src/ipa/rkisp1/algorithms/agc.h > +++ b/src/ipa/rkisp1/algorithms/agc.h > @@ -26,6 +26,10 @@ public: > ~Agc() = default; > > int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; > + void queueRequest(IPAContext &context, > + const uint32_t frame, > + IPAFrameContext &frameContext, > + const ControlList &controls) override; > void prepare(IPAContext &context, const uint32_t frame, > IPAFrameContext &frameContext, > rkisp1_params_cfg *params) override; > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h > index 3e47ac663c58..b9b2065328d6 100644 > --- a/src/ipa/rkisp1/ipa_context.h > +++ b/src/ipa/rkisp1/ipa_context.h > @@ -54,8 +54,16 @@ struct IPASessionConfiguration { > > struct IPAActiveState { > struct { > - uint32_t exposure; > - double gain; > + struct { > + uint32_t exposure; > + double gain; > + } manual; > + struct { > + uint32_t exposure; > + double gain; > + } automatic; > + > + bool autoEnabled; > } agc; > > struct { > @@ -96,6 +104,7 @@ struct IPAFrameContext : public FrameContext { > struct { > uint32_t exposure; > double gain; > + bool autoEnabled; > } agc; > > struct { > diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp > index 6ac29df8ec8d..76e818d2d11e 100644 > --- a/src/ipa/rkisp1/rkisp1.cpp > +++ b/src/ipa/rkisp1/rkisp1.cpp > @@ -158,6 +158,9 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, > return -ENODEV; > } > > + context_.configuration.sensor.lineDuration = sensorInfo.minLineLength > + * 1.0s / sensorInfo.pixelRate; > + > /* Load the tuning data file. */ > File file(settings.configurationFile); > if (!file.open(File::OpenModeFlag::ReadOnly)) { > @@ -377,6 +380,28 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo, > { > ControlInfoMap::Map ctrlMap = rkisp1Controls; > > + /* > + * Compute exposure time limits from the V4L2_CID_EXPOSURE control > + * limits and the line duration. > + */ > + double lineDuration = context_.configuration.sensor.lineDuration.get<std::micro>(); > + const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; > + int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration; > + int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration; > + int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration; > + ctrlMap.emplace(std::piecewise_construct, > + std::forward_as_tuple(&controls::ExposureTime), > + std::forward_as_tuple(minExposure, maxExposure, defExposure)); > + > + /* Compute the analogue gain limits. */ > + const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; > + float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>()); > + float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>()); > + float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>()); > + ctrlMap.emplace(std::piecewise_construct, > + std::forward_as_tuple(&controls::AnalogueGain), > + std::forward_as_tuple(minGain, maxGain, defGain)); > + > /* > * Compute the frame duration limits.
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 169afe372803..ccbcd4a9583d 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -74,9 +74,14 @@ Agc::Agc() int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) { /* Configure the default exposure and gain. */ - context.activeState.agc.gain = std::max(context.configuration.sensor.minAnalogueGain, - kMinAnalogueGain); - context.activeState.agc.exposure = 10ms / context.configuration.sensor.lineDuration; + context.activeState.agc.automatic.gain = + std::max(context.configuration.sensor.minAnalogueGain, + kMinAnalogueGain); + context.activeState.agc.automatic.exposure = + 10ms / context.configuration.sensor.lineDuration; + context.activeState.agc.manual.gain = context.activeState.agc.automatic.gain; + context.activeState.agc.manual.exposure = context.activeState.agc.automatic.exposure; + context.activeState.agc.autoEnabled = true; /* * According to the RkISP1 documentation: @@ -108,14 +113,58 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo) return 0; } +/** + * \copydoc libcamera::ipa::Algorithm::queueRequest + */ +void Agc::queueRequest(IPAContext &context, + [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) +{ + auto &agc = context.activeState.agc; + + const auto &agcEnable = controls.get(controls::AeEnable); + if (agcEnable && *agcEnable != agc.autoEnabled) { + agc.autoEnabled = *agcEnable; + + LOG(RkISP1Agc, Debug) + << (agc.autoEnabled ? "Enabling" : "Disabling") << " AGC"; + } + + const auto &exposure = controls.get(controls::ExposureTime); + if (exposure && !agc.autoEnabled) { + agc.manual.exposure = *exposure * 1.0us + / context.configuration.sensor.lineDuration; + + LOG(RkISP1Agc, Debug) + << "Set exposure to " << agc.manual.exposure; + } + + const auto &gain = controls.get(controls::AnalogueGain); + if (gain && !agc.autoEnabled) { + agc.manual.gain = *gain; + + LOG(RkISP1Agc, Debug) << "Set gain to " << agc.manual.gain; + } + + frameContext.agc.autoEnabled = agc.autoEnabled; + + if (!frameContext.agc.autoEnabled) { + frameContext.agc.exposure = agc.manual.exposure; + frameContext.agc.gain = agc.manual.gain; + } +} + /** * \copydoc libcamera::ipa::Algorithm::prepare */ void Agc::prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, rkisp1_params_cfg *params) { - frameContext.agc.exposure = context.activeState.agc.exposure; - frameContext.agc.gain = context.activeState.agc.gain; + if (frameContext.agc.autoEnabled) { + frameContext.agc.exposure = context.activeState.agc.automatic.exposure; + frameContext.agc.gain = context.activeState.agc.automatic.gain; + } if (frame > 0) return; @@ -263,8 +312,8 @@ void Agc::computeExposure(IPAContext &context, IPAFrameContext &frameContext, << stepGain; /* Update the estimated exposure and gain. */ - activeState.agc.exposure = shutterTime / configuration.sensor.lineDuration; - activeState.agc.gain = stepGain; + activeState.agc.automatic.exposure = shutterTime / configuration.sensor.lineDuration; + activeState.agc.automatic.gain = stepGain; } /** diff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h index da4d2d4e8359..a228d0c37768 100644 --- a/src/ipa/rkisp1/algorithms/agc.h +++ b/src/ipa/rkisp1/algorithms/agc.h @@ -26,6 +26,10 @@ public: ~Agc() = default; int configure(IPAContext &context, const IPACameraSensorInfo &configInfo) override; + void queueRequest(IPAContext &context, + const uint32_t frame, + IPAFrameContext &frameContext, + const ControlList &controls) override; void prepare(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, rkisp1_params_cfg *params) override; diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index 3e47ac663c58..b9b2065328d6 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -54,8 +54,16 @@ struct IPASessionConfiguration { struct IPAActiveState { struct { - uint32_t exposure; - double gain; + struct { + uint32_t exposure; + double gain; + } manual; + struct { + uint32_t exposure; + double gain; + } automatic; + + bool autoEnabled; } agc; struct { @@ -96,6 +104,7 @@ struct IPAFrameContext : public FrameContext { struct { uint32_t exposure; double gain; + bool autoEnabled; } agc; struct { diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp index 6ac29df8ec8d..76e818d2d11e 100644 --- a/src/ipa/rkisp1/rkisp1.cpp +++ b/src/ipa/rkisp1/rkisp1.cpp @@ -158,6 +158,9 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision, return -ENODEV; } + context_.configuration.sensor.lineDuration = sensorInfo.minLineLength + * 1.0s / sensorInfo.pixelRate; + /* Load the tuning data file. */ File file(settings.configurationFile); if (!file.open(File::OpenModeFlag::ReadOnly)) { @@ -377,6 +380,28 @@ void IPARkISP1::updateControls(const IPACameraSensorInfo &sensorInfo, { ControlInfoMap::Map ctrlMap = rkisp1Controls; + /* + * Compute exposure time limits from the V4L2_CID_EXPOSURE control + * limits and the line duration. + */ + double lineDuration = context_.configuration.sensor.lineDuration.get<std::micro>(); + const ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second; + int32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration; + int32_t maxExposure = v4l2Exposure.max().get<int32_t>() * lineDuration; + int32_t defExposure = v4l2Exposure.def().get<int32_t>() * lineDuration; + ctrlMap.emplace(std::piecewise_construct, + std::forward_as_tuple(&controls::ExposureTime), + std::forward_as_tuple(minExposure, maxExposure, defExposure)); + + /* Compute the analogue gain limits. */ + const ControlInfo &v4l2Gain = sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; + float minGain = camHelper_->gain(v4l2Gain.min().get<int32_t>()); + float maxGain = camHelper_->gain(v4l2Gain.max().get<int32_t>()); + float defGain = camHelper_->gain(v4l2Gain.def().get<int32_t>()); + ctrlMap.emplace(std::piecewise_construct, + std::forward_as_tuple(&controls::AnalogueGain), + std::forward_as_tuple(minGain, maxGain, defGain)); + /* * Compute the frame duration limits. *