[RFC,v3,20/22] libcamera: pipeline: Fill `MetadataListPlan` of cameras
diff mbox series

Message ID 20251030165816.1095180-21-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • libcamera: Add `MetadataList`
Related show

Commit Message

Barnabás Pőcze Oct. 30, 2025, 4:58 p.m. UTC
Fill the newly introduced `MetadataListPlan` member of the camera's private
data during initializations, similarly to the camera's `ControlInfoMap`.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
---
changes in v2:
  * add missing controls for `rkisp1`
  * sort alphabetically
---
 .../internal/software_isp/software_isp.h      |  3 +-
 include/libcamera/ipa/ipu3.mojom              |  3 +-
 include/libcamera/ipa/mali-c55.mojom          |  3 +-
 include/libcamera/ipa/raspberrypi.mojom       |  1 +
 include/libcamera/ipa/rkisp1.mojom            |  3 +-
 include/libcamera/ipa/soft.mojom              |  3 +-
 src/ipa/ipu3/algorithms/agc.cpp               |  4 +++
 src/ipa/ipu3/algorithms/awb.cpp               | 12 +++++++
 src/ipa/ipu3/algorithms/awb.h                 |  1 +
 src/ipa/ipu3/ipa_context.cpp                  |  3 ++
 src/ipa/ipu3/ipa_context.h                    |  3 ++
 src/ipa/ipu3/ipu3.cpp                         |  8 +++--
 src/ipa/mali-c55/algorithms/agc.cpp           |  5 +++
 src/ipa/mali-c55/algorithms/awb.cpp           |  7 ++++
 src/ipa/mali-c55/algorithms/awb.h             |  1 +
 src/ipa/mali-c55/algorithms/blc.cpp           |  2 ++
 src/ipa/mali-c55/ipa_context.h                |  3 ++
 src/ipa/mali-c55/mali-c55.cpp                 |  6 ++--
 src/ipa/rkisp1/algorithms/agc.cpp             | 10 ++++++
 src/ipa/rkisp1/algorithms/awb.cpp             |  4 +++
 src/ipa/rkisp1/algorithms/blc.cpp             |  2 ++
 src/ipa/rkisp1/algorithms/ccm.cpp             |  2 ++
 src/ipa/rkisp1/algorithms/goc.cpp             |  2 ++
 src/ipa/rkisp1/algorithms/lux.cpp             |  8 ++++-
 src/ipa/rkisp1/ipa_context.h                  |  1 +
 src/ipa/rkisp1/rkisp1.cpp                     |  8 +++--
 src/ipa/rpi/common/ipa_base.cpp               | 34 +++++++++++++++++++
 src/ipa/rpi/pisp/pisp.cpp                     |  5 +--
 src/ipa/rpi/vc4/vc4.cpp                       |  4 ++-
 src/ipa/simple/algorithms/agc.cpp             |  8 +++++
 src/ipa/simple/algorithms/agc.h               |  1 +
 src/ipa/simple/algorithms/awb.cpp             |  8 +++++
 src/ipa/simple/algorithms/awb.h               |  1 +
 src/ipa/simple/algorithms/blc.cpp             |  3 ++
 src/ipa/simple/algorithms/ccm.cpp             |  3 ++
 src/ipa/simple/algorithms/lut.cpp             |  3 ++
 src/ipa/simple/ipa_context.h                  |  2 ++
 src/ipa/simple/soft_simple.cpp                |  8 +++--
 src/libcamera/pipeline/imx8-isi/imx8-isi.cpp  |  2 ++
 src/libcamera/pipeline/ipu3/ipu3.cpp          |  7 +++-
 src/libcamera/pipeline/mali-c55/mali-c55.cpp  |  2 +-
 src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 ++-
 .../pipeline/rpi/common/pipeline_base.cpp     |  8 +++++
 src/libcamera/pipeline/simple/simple.cpp      |  2 +-
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  2 ++
 src/libcamera/pipeline/vimc/vimc.cpp          |  2 ++
 .../pipeline/virtual/config_parser.cpp        |  3 ++
 src/libcamera/software_isp/software_isp.cpp   |  6 ++--
 48 files changed, 203 insertions(+), 23 deletions(-)

Comments

Kieran Bingham Nov. 2, 2025, 3:50 p.m. UTC | #1
Quoting Barnabás Pőcze (2025-10-30 16:58:14)
> Fill the newly introduced `MetadataListPlan` member of the camera's private
> data during initializations, similarly to the camera's `ControlInfoMap`.
> 
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>

Not much to say for this one except it would need to be double checked
before merging.

Though I guess we'll get errors if we try to add metadata when it's not
in the plan ?


Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

> ---
> changes in v2:
>   * add missing controls for `rkisp1`
>   * sort alphabetically
> ---
>  .../internal/software_isp/software_isp.h      |  3 +-
>  include/libcamera/ipa/ipu3.mojom              |  3 +-
>  include/libcamera/ipa/mali-c55.mojom          |  3 +-
>  include/libcamera/ipa/raspberrypi.mojom       |  1 +
>  include/libcamera/ipa/rkisp1.mojom            |  3 +-
>  include/libcamera/ipa/soft.mojom              |  3 +-
>  src/ipa/ipu3/algorithms/agc.cpp               |  4 +++
>  src/ipa/ipu3/algorithms/awb.cpp               | 12 +++++++
>  src/ipa/ipu3/algorithms/awb.h                 |  1 +
>  src/ipa/ipu3/ipa_context.cpp                  |  3 ++
>  src/ipa/ipu3/ipa_context.h                    |  3 ++
>  src/ipa/ipu3/ipu3.cpp                         |  8 +++--
>  src/ipa/mali-c55/algorithms/agc.cpp           |  5 +++
>  src/ipa/mali-c55/algorithms/awb.cpp           |  7 ++++
>  src/ipa/mali-c55/algorithms/awb.h             |  1 +
>  src/ipa/mali-c55/algorithms/blc.cpp           |  2 ++
>  src/ipa/mali-c55/ipa_context.h                |  3 ++
>  src/ipa/mali-c55/mali-c55.cpp                 |  6 ++--
>  src/ipa/rkisp1/algorithms/agc.cpp             | 10 ++++++
>  src/ipa/rkisp1/algorithms/awb.cpp             |  4 +++
>  src/ipa/rkisp1/algorithms/blc.cpp             |  2 ++
>  src/ipa/rkisp1/algorithms/ccm.cpp             |  2 ++
>  src/ipa/rkisp1/algorithms/goc.cpp             |  2 ++
>  src/ipa/rkisp1/algorithms/lux.cpp             |  8 ++++-
>  src/ipa/rkisp1/ipa_context.h                  |  1 +
>  src/ipa/rkisp1/rkisp1.cpp                     |  8 +++--
>  src/ipa/rpi/common/ipa_base.cpp               | 34 +++++++++++++++++++
>  src/ipa/rpi/pisp/pisp.cpp                     |  5 +--
>  src/ipa/rpi/vc4/vc4.cpp                       |  4 ++-
>  src/ipa/simple/algorithms/agc.cpp             |  8 +++++
>  src/ipa/simple/algorithms/agc.h               |  1 +
>  src/ipa/simple/algorithms/awb.cpp             |  8 +++++
>  src/ipa/simple/algorithms/awb.h               |  1 +
>  src/ipa/simple/algorithms/blc.cpp             |  3 ++
>  src/ipa/simple/algorithms/ccm.cpp             |  3 ++
>  src/ipa/simple/algorithms/lut.cpp             |  3 ++
>  src/ipa/simple/ipa_context.h                  |  2 ++
>  src/ipa/simple/soft_simple.cpp                |  8 +++--
>  src/libcamera/pipeline/imx8-isi/imx8-isi.cpp  |  2 ++
>  src/libcamera/pipeline/ipu3/ipu3.cpp          |  7 +++-
>  src/libcamera/pipeline/mali-c55/mali-c55.cpp  |  2 +-
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 ++-
>  .../pipeline/rpi/common/pipeline_base.cpp     |  8 +++++
>  src/libcamera/pipeline/simple/simple.cpp      |  2 +-
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  2 ++
>  src/libcamera/pipeline/vimc/vimc.cpp          |  2 ++
>  .../pipeline/virtual/config_parser.cpp        |  3 ++
>  src/libcamera/software_isp/software_isp.cpp   |  6 ++--
>  48 files changed, 203 insertions(+), 23 deletions(-)
> 
> diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
> index 7862465924..bd85f6b4bd 100644
> --- a/include/libcamera/internal/software_isp/software_isp.h
> +++ b/include/libcamera/internal/software_isp/software_isp.h
> @@ -41,6 +41,7 @@ class DebayerCpu;
>  class FrameBuffer;
>  class PixelFormat;
>  class Stream;
> +class MetadataListPlan;
>  struct StreamConfiguration;
>  
>  LOG_DECLARE_CATEGORY(SoftwareIsp)
> @@ -49,7 +50,7 @@ class SoftwareIsp : public Object
>  {
>  public:
>         SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
> -                   ControlInfoMap *ipaControls);
> +                   ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan);
>         ~SoftwareIsp();
>  
>         int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
> diff --git a/include/libcamera/ipa/ipu3.mojom b/include/libcamera/ipa/ipu3.mojom
> index d9a50b01db..f49b777973 100644
> --- a/include/libcamera/ipa/ipu3.mojom
> +++ b/include/libcamera/ipa/ipu3.mojom
> @@ -20,7 +20,8 @@ interface IPAIPU3Interface {
>         init(libcamera.IPASettings settings,
>              libcamera.IPACameraSensorInfo sensorInfo,
>              libcamera.ControlInfoMap sensorControls)
> -               => (int32 ret, libcamera.ControlInfoMap ipaControls);
> +               => (int32 ret, libcamera.ControlInfoMap ipaControls,
> +                   libcamera.MetadataListPlan metadata);
>         start() => (int32 ret);
>         stop();
>  
> diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom
> index 39b7f1f109..af1d083728 100644
> --- a/include/libcamera/ipa/mali-c55.mojom
> +++ b/include/libcamera/ipa/mali-c55.mojom
> @@ -11,7 +11,8 @@ struct IPAConfigInfo {
>  
>  interface IPAMaliC55Interface {
>         init(libcamera.IPASettings settings, IPAConfigInfo configInfo)
> -               => (int32 ret, libcamera.ControlInfoMap ipaControls);
> +               => (int32 ret, libcamera.ControlInfoMap ipaControls,
> +                   libcamera.MetadataListPlan metadataPlan);
>         start() => (int32 ret);
>         stop();
>  
> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
> index 12b083e9d0..c32391911e 100644
> --- a/include/libcamera/ipa/raspberrypi.mojom
> +++ b/include/libcamera/ipa/raspberrypi.mojom
> @@ -26,6 +26,7 @@ struct InitParams {
>  struct InitResult {
>         SensorConfig sensorConfig;
>         libcamera.ControlInfoMap controlInfo;
> +       libcamera.MetadataListPlan metadataPlan;
>  };
>  
>  struct BufferIds {
> diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom
> index 068e898848..015ba46036 100644
> --- a/include/libcamera/ipa/rkisp1.mojom
> +++ b/include/libcamera/ipa/rkisp1.mojom
> @@ -19,7 +19,8 @@ interface IPARkISP1Interface {
>              uint32 hwRevision, uint32 supportedBlocks,
>              libcamera.IPACameraSensorInfo sensorInfo,
>              libcamera.ControlInfoMap sensorControls)
> -               => (int32 ret, libcamera.ControlInfoMap ipaControls);
> +               => (int32 ret, libcamera.ControlInfoMap ipaControls,
> +                   libcamera.MetadataListPlan metadataPlan);
>         start() => (int32 ret);
>         stop();
>  
> diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
> index 77328c5fd5..360ed668b7 100644
> --- a/include/libcamera/ipa/soft.mojom
> +++ b/include/libcamera/ipa/soft.mojom
> @@ -18,7 +18,8 @@ interface IPASoftInterface {
>              libcamera.SharedFD fdParams,
>              libcamera.IPACameraSensorInfo sensorInfo,
>              libcamera.ControlInfoMap sensorControls)
> -               => (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled);
> +               => (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled,
> +                   libcamera.MetadataListPlan metadataPlan);
>         start() => (int32 ret);
>         stop();
>         configure(IPAConfigInfo configInfo)
> diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
> index b0d89541da..2d7b0482fe 100644
> --- a/src/ipa/ipu3/algorithms/agc.cpp
> +++ b/src/ipa/ipu3/algorithms/agc.cpp
> @@ -82,6 +82,10 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData)
>  
>         context.ctrlMap.merge(controls());
>  
> +       context.metadataPlan.set(controls::AnalogueGain);
> +       context.metadataPlan.set(controls::ExposureTime);
> +       context.metadataPlan.set(controls::FrameDuration);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp
> index 55de05d9e3..499c7790f9 100644
> --- a/src/ipa/ipu3/algorithms/awb.cpp
> +++ b/src/ipa/ipu3/algorithms/awb.cpp
> @@ -197,6 +197,18 @@ Awb::Awb()
>  
>  Awb::~Awb() = default;
>  
> +/**
> + * \copydoc libcamera::ipa::Algorithm::init
> + */
> +int Awb::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
> +{
> +       context.metadataPlan.set(controls::AwbEnable);
> +       context.metadataPlan.set(controls::ColourGains);
> +       context.metadataPlan.set(controls::ColourTemperature);
> +
> +       return 0;
> +}
> +
>  /**
>   * \copydoc libcamera::ipa::Algorithm::configure
>   */
> diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h
> index dbf69c9073..1d47bffa34 100644
> --- a/src/ipa/ipu3/algorithms/awb.h
> +++ b/src/ipa/ipu3/algorithms/awb.h
> @@ -40,6 +40,7 @@ public:
>         Awb();
>         ~Awb();
>  
> +       int init(IPAContext &context, const YamlObject &tuningData) override;
>         int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
>         void prepare(IPAContext &context, const uint32_t frame,
>                      IPAFrameContext &frameContext,
> diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp
> index 3b22f79176..f0b8b5dbea 100644
> --- a/src/ipa/ipu3/ipa_context.cpp
> +++ b/src/ipa/ipu3/ipa_context.cpp
> @@ -54,6 +54,9 @@ namespace libcamera::ipa::ipu3 {
>   *
>   * \var IPAContext::ctrlMap
>   * \brief A ControlInfoMap::Map of controls populated by the algorithms
> + *
> + * \var IPAContext::metadataPlan
> + * \brief A MetadataListPlan populated by the algorithms
>   */
>  
>  /**
> diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h
> index 97fcf06cd4..f4f45ef4d4 100644
> --- a/src/ipa/ipu3/ipa_context.h
> +++ b/src/ipa/ipu3/ipa_context.h
> @@ -14,6 +14,7 @@
>  
>  #include <libcamera/controls.h>
>  #include <libcamera/geometry.h>
> +#include <libcamera/metadata_list_plan.h>
>  
>  #include <libipa/fc_queue.h>
>  
> @@ -95,6 +96,8 @@ struct IPAContext {
>         FCQueue<IPAFrameContext> frameContexts;
>  
>         ControlInfoMap::Map ctrlMap;
> +
> +       MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
>  };
>  
>  } /* namespace ipa::ipu3 */
> diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
> index 1cae08bf25..8d8811f717 100644
> --- a/src/ipa/ipu3/ipu3.cpp
> +++ b/src/ipa/ipu3/ipu3.cpp
> @@ -143,7 +143,8 @@ public:
>         int init(const IPASettings &settings,
>                  const IPACameraSensorInfo &sensorInfo,
>                  const ControlInfoMap &sensorControls,
> -                ControlInfoMap *ipaControls) override;
> +                ControlInfoMap *ipaControls,
> +                MetadataListPlan *metadataPlan) override;
>  
>         int start() override;
>         void stop() override;
> @@ -299,7 +300,8 @@ void IPAIPU3::updateControls(const IPACameraSensorInfo &sensorInfo,
>  int IPAIPU3::init(const IPASettings &settings,
>                   const IPACameraSensorInfo &sensorInfo,
>                   const ControlInfoMap &sensorControls,
> -                 ControlInfoMap *ipaControls)
> +                 ControlInfoMap *ipaControls,
> +                 MetadataListPlan *metadataPlan)
>  {
>         camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
>         if (camHelper_ == nullptr) {
> @@ -348,6 +350,8 @@ int IPAIPU3::init(const IPASettings &settings,
>         /* Initialize controls. */
>         updateControls(sensorInfo, sensorControls, ipaControls);
>  
> +       *metadataPlan = std::move(context_.metadataPlan);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
> index f60fddac3f..97316ca35c 100644
> --- a/src/ipa/mali-c55/algorithms/agc.cpp
> +++ b/src/ipa/mali-c55/algorithms/agc.cpp
> @@ -145,6 +145,11 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData)
>         );
>         context.ctrlMap.merge(controls());
>  
> +       context.metadataPlan.set(controls::AnalogueGain);
> +       context.metadataPlan.set(controls::ColourTemperature);
> +       context.metadataPlan.set(controls::DigitalGain);
> +       context.metadataPlan.set(controls::ExposureTime);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
> index 3d546e5a85..1fd67a4f9f 100644
> --- a/src/ipa/mali-c55/algorithms/awb.cpp
> +++ b/src/ipa/mali-c55/algorithms/awb.cpp
> @@ -29,6 +29,13 @@ Awb::Awb()
>  {
>  }
>  
> +int Awb::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
> +{
> +       context.metadataPlan.set(controls::ColourGains);
> +
> +       return 0;
> +}
> +
>  int Awb::configure([[maybe_unused]] IPAContext &context,
>                    [[maybe_unused]] const IPACameraSensorInfo &configInfo)
>  {
> diff --git a/src/ipa/mali-c55/algorithms/awb.h b/src/ipa/mali-c55/algorithms/awb.h
> index 2351d40555..6bdc88a193 100644
> --- a/src/ipa/mali-c55/algorithms/awb.h
> +++ b/src/ipa/mali-c55/algorithms/awb.h
> @@ -18,6 +18,7 @@ public:
>         Awb();
>         ~Awb() = default;
>  
> +       int init(IPAContext &context, const YamlObject &tuningData) override;
>         int configure(IPAContext &context,
>                       const IPACameraSensorInfo &configInfo) override;
>         void prepare(IPAContext &context, const uint32_t frame,
> diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp
> index 2a54c86a91..d36c7ac66e 100644
> --- a/src/ipa/mali-c55/algorithms/blc.cpp
> +++ b/src/ipa/mali-c55/algorithms/blc.cpp
> @@ -51,6 +51,8 @@ int BlackLevelCorrection::init([[maybe_unused]] IPAContext &context,
>  
>         tuningParameters_ = true;
>  
> +       context.metadataPlan.set(controls::SensorBlackLevels);
> +
>         LOG(MaliC55Blc, Debug)
>                 << "Black levels: 00 " << offset00 << ", 01 " << offset01
>                 << ", 10 " << offset10 << ", 11 " << offset11;
> diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
> index 13885eb83b..55aef1d100 100644
> --- a/src/ipa/mali-c55/ipa_context.h
> +++ b/src/ipa/mali-c55/ipa_context.h
> @@ -9,6 +9,7 @@
>  
>  #include <libcamera/base/utils.h>
>  #include <libcamera/controls.h>
> +#include <libcamera/metadata_list_plan.h>
>  
>  #include "libcamera/internal/bayer_format.h"
>  
> @@ -83,6 +84,8 @@ struct IPAContext {
>         FCQueue<IPAFrameContext> frameContexts;
>  
>         ControlInfoMap::Map ctrlMap;
> +
> +       MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
>  };
>  
>  } /* namespace ipa::mali_c55 */
> diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp
> index 7d45e7310a..10b61b4fb3 100644
> --- a/src/ipa/mali-c55/mali-c55.cpp
> +++ b/src/ipa/mali-c55/mali-c55.cpp
> @@ -46,7 +46,7 @@ public:
>         IPAMaliC55();
>  
>         int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,
> -                ControlInfoMap *ipaControls) override;
> +                ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan) override;
>         int start() override;
>         void stop() override;
>         int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,
> @@ -96,7 +96,7 @@ std::string IPAMaliC55::logPrefix() const
>  }
>  
>  int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,
> -                    ControlInfoMap *ipaControls)
> +                    ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan)
>  {
>         camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
>         if (!camHelper_) {
> @@ -131,6 +131,8 @@ int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig
>  
>         updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);
>  
> +       *metadataPlan = std::move(context_.metadataPlan);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
> index f5a3c917cb..cbc7311cd5 100644
> --- a/src/ipa/rkisp1/algorithms/agc.cpp
> +++ b/src/ipa/rkisp1/algorithms/agc.cpp
> @@ -160,6 +160,16 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData)
>         context.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);
>         context.ctrlMap.merge(controls());
>  
> +       context.metadataPlan.set(controls::AeConstraintMode);
> +       context.metadataPlan.set(controls::AeExposureMode);
> +       context.metadataPlan.set(controls::AeMeteringMode);
> +       context.metadataPlan.set(controls::AnalogueGain);
> +       context.metadataPlan.set(controls::AnalogueGainMode);
> +       context.metadataPlan.set(controls::ExposureTime);
> +       context.metadataPlan.set(controls::ExposureTimeMode);
> +       context.metadataPlan.set(controls::ExposureValue);
> +       context.metadataPlan.set(controls::FrameDuration);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp
> index 399fb51be4..8de7ea3951 100644
> --- a/src/ipa/rkisp1/algorithms/awb.cpp
> +++ b/src/ipa/rkisp1/algorithms/awb.cpp
> @@ -117,6 +117,10 @@ int Awb::init(IPAContext &context, const YamlObject &tuningData)
>         const auto &src = awbAlgo_->controls();
>         cmap.insert(src.begin(), src.end());
>  
> +       context.metadataPlan.set(controls::AwbEnable);
> +       context.metadataPlan.set(controls::ColourGains);
> +       context.metadataPlan.set(controls::ColourTemperature);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp
> index 32fc44ffff..03071360b3 100644
> --- a/src/ipa/rkisp1/algorithms/blc.cpp
> +++ b/src/ipa/rkisp1/algorithms/blc.cpp
> @@ -103,6 +103,8 @@ int BlackLevelCorrection::init(IPAContext &context, const YamlObject &tuningData
>                 << ", green (blue) " << blackLevelGreenB_
>                 << ", blue " << blackLevelBlue_;
>  
> +       context.metadataPlan.set(controls::SensorBlackLevels);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp
> index de2b6fe775..3115d3effa 100644
> --- a/src/ipa/rkisp1/algorithms/ccm.cpp
> +++ b/src/ipa/rkisp1/algorithms/ccm.cpp
> @@ -66,6 +66,8 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
>                 offsets_.setData({ { 0, Matrix<int16_t, 3, 1>({ 0, 0, 0 }) } });
>         }
>  
> +       context.metadataPlan.set(controls::ColourCorrectionMatrix);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
> index a0e7030fe5..46a4c2c364 100644
> --- a/src/ipa/rkisp1/algorithms/goc.cpp
> +++ b/src/ipa/rkisp1/algorithms/goc.cpp
> @@ -60,6 +60,8 @@ int GammaOutCorrection::init(IPAContext &context, const YamlObject &tuningData)
>         defaultGamma_ = tuningData["gamma"].get<double>(kDefaultGamma);
>         context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);
>  
> +       context.metadataPlan.set(controls::Gamma);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rkisp1/algorithms/lux.cpp b/src/ipa/rkisp1/algorithms/lux.cpp
> index e8da698100..966c711375 100644
> --- a/src/ipa/rkisp1/algorithms/lux.cpp
> +++ b/src/ipa/rkisp1/algorithms/lux.cpp
> @@ -43,7 +43,13 @@ Lux::Lux()
>   */
>  int Lux::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
>  {
> -       return lux_.parseTuningData(tuningData);
> +       int ret = lux_.parseTuningData(tuningData);
> +       if (ret)
> +               return ret;
> +
> +       context.metadataPlan.set(controls::Lux);
> +
> +       return 0;
>  }
>  
>  /**
> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> index f85a130d9c..caf7d9d448 100644
> --- a/src/ipa/rkisp1/ipa_context.h
> +++ b/src/ipa/rkisp1/ipa_context.h
> @@ -230,6 +230,7 @@ struct IPAContext {
>         FCQueue<IPAFrameContext> frameContexts;
>  
>         ControlInfoMap::Map ctrlMap;
> +       MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
>  
>         DebugMetadata debugMetadata;
>  
> diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
> index fa22bfc349..9815b83a03 100644
> --- a/src/ipa/rkisp1/rkisp1.cpp
> +++ b/src/ipa/rkisp1/rkisp1.cpp
> @@ -55,7 +55,8 @@ public:
>                  uint32_t supportedBlocks,
>                  const IPACameraSensorInfo &sensorInfo,
>                  const ControlInfoMap &sensorControls,
> -                ControlInfoMap *ipaControls) override;
> +                ControlInfoMap *ipaControls,
> +                MetadataListPlan *metadataPlan) override;
>         int start() override;
>         void stop() override;
>  
> @@ -139,7 +140,8 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
>                     uint32_t supportedBlocks,
>                     const IPACameraSensorInfo &sensorInfo,
>                     const ControlInfoMap &sensorControls,
> -                   ControlInfoMap *ipaControls)
> +                   ControlInfoMap *ipaControls,
> +                   MetadataListPlan *metadataPlan)
>  {
>         /* \todo Add support for other revisions */
>         switch (hwRevision) {
> @@ -209,6 +211,8 @@ int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
>         /* Initialize controls. */
>         updateControls(sensorInfo, sensorControls, ipaControls);
>  
> +       *metadataPlan = std::move(context_.metadataPlan);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
> index 8dfe35cc32..15e65f9315 100644
> --- a/src/ipa/rpi/common/ipa_base.cpp
> +++ b/src/ipa/rpi/common/ipa_base.cpp
> @@ -182,6 +182,40 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini
>  
>         result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
>  
> +       // TODO: only set those that can be reported by configured algorithms?
> +       // TODO: move this somewhere else?
> +       result->metadataPlan.set(controls::AeConstraintMode);
> +       result->metadataPlan.set(controls::AeExposureMode);
> +       result->metadataPlan.set(controls::AeMeteringMode);
> +       result->metadataPlan.set(controls::AeState);
> +       result->metadataPlan.set(controls::AfPauseState);
> +       result->metadataPlan.set(controls::AfState);
> +       result->metadataPlan.set(controls::AnalogueGain);
> +       result->metadataPlan.set(controls::AnalogueGainMode);
> +       result->metadataPlan.set(controls::AwbEnable);
> +       result->metadataPlan.set(controls::AwbMode);
> +       result->metadataPlan.set(controls::Brightness);
> +       result->metadataPlan.set(controls::ColourCorrectionMatrix);
> +       result->metadataPlan.set(controls::ColourGains);
> +       result->metadataPlan.set(controls::ColourTemperature);
> +       result->metadataPlan.set(controls::Contrast);
> +       result->metadataPlan.set(controls::DigitalGain);
> +       result->metadataPlan.set(controls::ExposureTime);
> +       result->metadataPlan.set(controls::ExposureTimeMode);
> +       result->metadataPlan.set(controls::ExposureValue);
> +       result->metadataPlan.set(controls::FocusFoM);
> +       result->metadataPlan.set(controls::FrameDuration);
> +       result->metadataPlan.set(controls::FrameDurationLimits);
> +       result->metadataPlan.set(controls::HdrChannel);
> +       result->metadataPlan.set(controls::HdrMode);
> +       result->metadataPlan.set(controls::LensPosition);
> +       result->metadataPlan.set(controls::Lux);
> +       result->metadataPlan.set(controls::Saturation);
> +       result->metadataPlan.set(controls::SensorBlackLevels);
> +       result->metadataPlan.set(controls::SensorTemperature);
> +       result->metadataPlan.set(controls::Sharpness);
> +       result->metadataPlan.set(controls::draft::NoiseReductionMode);
> +
>         return platformInit(params, result);
>  }
>  
> diff --git a/src/ipa/rpi/pisp/pisp.cpp b/src/ipa/rpi/pisp/pisp.cpp
> index ec7593ffc9..04e7731d22 100644
> --- a/src/ipa/rpi/pisp/pisp.cpp
> +++ b/src/ipa/rpi/pisp/pisp.cpp
> @@ -291,8 +291,7 @@ private:
>         HdrStatus lastStitchHdrStatus_;
>  };
>  
> -int32_t IpaPiSP::platformInit(const InitParams &params,
> -                             [[maybe_unused]] InitResult *result)
> +int32_t IpaPiSP::platformInit(const InitParams &params, InitResult *result)
>  {
>         const std::string &target = controller_.getTarget();
>         if (target != "pisp") {
> @@ -325,6 +324,8 @@ int32_t IpaPiSP::platformInit(const InitParams &params,
>  
>         setDefaultConfig();
>  
> +       result->metadataPlan.set(controls::rpi::PispStatsOutput, sizeof(pisp_statistics));
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp
> index 2b205b2861..3014925eaf 100644
> --- a/src/ipa/rpi/vc4/vc4.cpp
> +++ b/src/ipa/rpi/vc4/vc4.cpp
> @@ -91,7 +91,7 @@ private:
>         AwbStatus lastAwbStatus_;
>  };
>  
> -int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, [[maybe_unused]] InitResult *result)
> +int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, InitResult *result)
>  {
>         const std::string &target = controller_.getTarget();
>  
> @@ -102,6 +102,8 @@ int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, [[maybe_
>                 return -EINVAL;
>         }
>  
> +       result->metadataPlan.set(controls::rpi::Bcm2835StatsOutput, sizeof(bcm2835_isp_stats));
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
> index 189de770c6..c4e52328f0 100644
> --- a/src/ipa/simple/algorithms/agc.cpp
> +++ b/src/ipa/simple/algorithms/agc.cpp
> @@ -41,6 +41,14 @@ Agc::Agc()
>  {
>  }
>  
> +int Agc::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
> +{
> +       context.metadataPlan.set(controls::AnalogueGain);
> +       context.metadataPlan.set(controls::ExposureTime);
> +
> +       return 0;
> +}
> +
>  void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV)
>  {
>         /*
> diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
> index 112d9f5a19..00e70ea705 100644
> --- a/src/ipa/simple/algorithms/agc.h
> +++ b/src/ipa/simple/algorithms/agc.h
> @@ -19,6 +19,7 @@ public:
>         Agc();
>         ~Agc() = default;
>  
> +       int init(IPAContext &context, const YamlObject &tuningData) override;
>         void process(IPAContext &context, const uint32_t frame,
>                      IPAFrameContext &frameContext,
>                      const SwIspStats *stats,
> diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp
> index cf78e98009..fe605117ab 100644
> --- a/src/ipa/simple/algorithms/awb.cpp
> +++ b/src/ipa/simple/algorithms/awb.cpp
> @@ -26,6 +26,14 @@ LOG_DEFINE_CATEGORY(IPASoftAwb)
>  
>  namespace ipa::soft::algorithms {
>  
> +int Awb::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
> +{
> +       context.metadataPlan.set(controls::ColourGains);
> +       context.metadataPlan.set(controls::ColourTemperature);
> +
> +       return 0;
> +}
> +
>  int Awb::configure(IPAContext &context,
>                    [[maybe_unused]] const IPAConfigInfo &configInfo)
>  {
> diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h
> index ad993f39c1..b8ae63dcb1 100644
> --- a/src/ipa/simple/algorithms/awb.h
> +++ b/src/ipa/simple/algorithms/awb.h
> @@ -19,6 +19,7 @@ public:
>         Awb() = default;
>         ~Awb() = default;
>  
> +       int init(IPAContext &context, const YamlObject &tuningData) override;
>         int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
>         void prepare(IPAContext &context,
>                      const uint32_t frame,
> diff --git a/src/ipa/simple/algorithms/blc.cpp b/src/ipa/simple/algorithms/blc.cpp
> index 370385afc6..bcb67b832a 100644
> --- a/src/ipa/simple/algorithms/blc.cpp
> +++ b/src/ipa/simple/algorithms/blc.cpp
> @@ -34,6 +34,9 @@ int BlackLevel::init([[maybe_unused]] IPAContext &context,
>                  */
>                 definedLevel_ = blackLevel.value() >> 8;
>         }
> +
> +       context.metadataPlan.set(controls::SensorBlackLevels);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
> index 0a98406c1a..b1758ff39b 100644
> --- a/src/ipa/simple/algorithms/ccm.cpp
> +++ b/src/ipa/simple/algorithms/ccm.cpp
> @@ -39,6 +39,9 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
>         context.ccmEnabled = true;
>         context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
>  
> +       context.metadataPlan.set(controls::ColourCorrectionMatrix);
> +       context.metadataPlan.set(controls::Saturation);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp
> index d1d5f72712..5a7ffbd8fa 100644
> --- a/src/ipa/simple/algorithms/lut.cpp
> +++ b/src/ipa/simple/algorithms/lut.cpp
> @@ -28,6 +28,9 @@ int Lut::init(IPAContext &context,
>               [[maybe_unused]] const YamlObject &tuningData)
>  {
>         context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f);
> +
> +       context.metadataPlan.set(controls::Contrast);
> +
>         return 0;
>  }
>  
> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
> index c3081e3069..d76c0f79ee 100644
> --- a/src/ipa/simple/ipa_context.h
> +++ b/src/ipa/simple/ipa_context.h
> @@ -12,6 +12,7 @@
>  #include <stdint.h>
>  
>  #include <libcamera/controls.h>
> +#include <libcamera/metadata_list_plan.h>
>  
>  #include "libcamera/internal/matrix.h"
>  #include "libcamera/internal/vector.h"
> @@ -102,6 +103,7 @@ struct IPAContext {
>         IPAActiveState activeState;
>         FCQueue<IPAFrameContext> frameContexts;
>         ControlInfoMap::Map ctrlMap;
> +       MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
>         bool ccmEnabled = false;
>  };
>  
> diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
> index b147aca2e3..9d698a27b3 100644
> --- a/src/ipa/simple/soft_simple.cpp
> +++ b/src/ipa/simple/soft_simple.cpp
> @@ -56,7 +56,8 @@ public:
>                  const IPACameraSensorInfo &sensorInfo,
>                  const ControlInfoMap &sensorControls,
>                  ControlInfoMap *ipaControls,
> -                bool *ccmEnabled) override;
> +                bool *ccmEnabled,
> +                MetadataListPlan *metadataPlan) override;
>         int configure(const IPAConfigInfo &configInfo) override;
>  
>         int start() override;
> @@ -96,7 +97,8 @@ int IPASoftSimple::init(const IPASettings &settings,
>                         const IPACameraSensorInfo &sensorInfo,
>                         const ControlInfoMap &sensorControls,
>                         ControlInfoMap *ipaControls,
> -                       bool *ccmEnabled)
> +                       bool *ccmEnabled,
> +                       MetadataListPlan *metadataPlan)
>  {
>         camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
>         if (!camHelper_) {
> @@ -190,6 +192,8 @@ int IPASoftSimple::init(const IPASettings &settings,
>                 return -EINVAL;
>         }
>  
> +       *metadataPlan = std::move(context_.metadataPlan);
> +
>         return 0;
>  }
>  
> diff --git a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
> index de09431cb9..fbad880b23 100644
> --- a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
> +++ b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
> @@ -166,6 +166,8 @@ int ISICameraData::init()
>  
>         properties_ = sensor_->properties();
>  
> +       metadataPlan_.set(controls::SensorTimestamp);
> +
>         return 0;
>  }
>  
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index d6b7edcb5a..13dbdb6268 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -1082,6 +1082,11 @@ int PipelineHandlerIPU3::registerCameras()
>                 if (ret)
>                         continue;
>  
> +               data->metadataPlan_.set(controls::draft::PipelineDepth);
> +               data->metadataPlan_.set(controls::draft::TestPatternMode);
> +               data->metadataPlan_.set(controls::ScalerCrop);
> +               data->metadataPlan_.set(controls::SensorTimestamp);
> +
>                 const CameraSensorProperties::SensorDelays &delays = cio2->sensor()->sensorDelays();
>                 std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
>                         { V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
> @@ -1191,7 +1196,7 @@ int IPU3CameraData::loadIPA()
>                 ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");
>  
>         ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
> -                        sensorInfo, sensor->controls(), &ipaControls_);
> +                        sensorInfo, sensor->controls(), &ipaControls_, &metadataPlan_);
>         if (ret) {
>                 LOG(IPU3, Error) << "Failed to initialise the IPU3 IPA";
>                 return ret;
> diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
> index 38bdc6138e..938c5b2890 100644
> --- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp
> +++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
> @@ -402,7 +402,7 @@ int MaliC55CameraData::loadIPA()
>  
>         ControlInfoMap ipaControls;
>         ret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig,
> -                        &ipaControls);
> +                        &ipaControls, &metadataPlan_);
>         if (ret) {
>                 LOG(MaliC55, Error) << "Failed to initialise the Mali-C55 IPA";
>                 return ret;
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index ecd1383153..002a44f304 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -406,7 +406,7 @@ int RkISP1CameraData::loadIPA(unsigned int hwRevision, uint32_t supportedBlocks)
>  
>         ret = ipa_->init({ ipaTuningFile, sensor_->model() }, hwRevision,
>                          supportedBlocks, sensorInfo, sensor_->controls(),
> -                        &ipaControls_);
> +                        &ipaControls_, &metadataPlan_);
>         if (ret < 0) {
>                 LOG(RkISP1, Error) << "IPA initialization failure";
>                 return ret;
> @@ -1377,6 +1377,8 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
>  
>         updateControls(data.get());
>  
> +       data->metadataPlan_.set(controls::SensorTimestamp);
> +
>         std::set<Stream *> streams{
>                 &data->mainPathStream_,
>                 &data->selfPathStream_,
> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> index c209aa5963..87ce290225 100644
> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> @@ -589,6 +589,9 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
>  
>         data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
>  
> +       /* Update `rpi::ScalerCrops` size for the corrent configuration. */
> +       data->metadataPlan_.set(controls::rpi::ScalerCrops, config->size());
> +
>         /* Setup the Video Mux/Bridge entities. */
>         for (auto &[device, link] : data->bridgeDevices_) {
>                 /*
> @@ -835,6 +838,11 @@ int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
>         /* Initialize the camera properties. */
>         data->properties_ = data->sensor_->properties();
>  
> +       data->metadataPlan_ = std::move(result.metadataPlan);
> +       data->metadataPlan_.set(controls::SensorTimestamp);
> +       data->metadataPlan_.set(controls::FrameWallClock);
> +       data->metadataPlan_.set(controls::ScalerCrop);
> +
>         /*
>          * The V4L2_CID_NOTIFY_GAINS control, if present, is used to inform the
>          * sensor of the colour gains. It is defined to be a linear gain where
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 91715b7f8a..312f35ad15 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -602,7 +602,7 @@ int SimpleCameraData::init()
>          * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
>          */
>         if (!converter_ && pipe->swIspEnabled()) {
> -               swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get(), &controlInfo_);
> +               swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get(), &controlInfo_, &metadataPlan_);
>                 if (!swIsp_->isValid()) {
>                         LOG(SimplePipeline, Warning)
>                                 << "Failed to create software ISP, disabling software debayering";
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> index 4b5816dfdd..59fb4bd5c9 100644
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> @@ -614,6 +614,8 @@ int UVCCameraData::init(MediaDevice *media)
>  
>         controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);
>  
> +       metadataPlan_.set(controls::SensorTimestamp);
> +
>         /*
>          * Close to allow camera to go into runtime-suspend, video_ will be
>          * re-opened from acquireDevice() and validate().
> diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> index 5022101505..1b6880a5dc 100644
> --- a/src/libcamera/pipeline/vimc/vimc.cpp
> +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> @@ -593,6 +593,8 @@ int VimcCameraData::init()
>  
>         controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);
>  
> +       metadataPlan_.set(controls::SensorTimestamp);
> +
>         /* Initialize the camera properties. */
>         properties_ = sensor_->properties();
>  
> diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp
> index 1d3d9ba87e..608bbd469f 100644
> --- a/src/libcamera/pipeline/virtual/config_parser.cpp
> +++ b/src/libcamera/pipeline/virtual/config_parser.cpp
> @@ -65,6 +65,9 @@ ConfigParser::parseConfigFile(File &file, PipelineHandler *pipe)
>                 controls[&controls::draft::FaceDetectMode] = ControlInfo(supportedFaceDetectModes);
>  
>                 data->controlInfo_ = ControlInfoMap(std::move(controls), controls::controls);
> +
> +               data->metadataPlan_.set(controls::SensorTimestamp);
> +
>                 configurations.push_back(std::move(data));
>         }
>  
> diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
> index fdadf79e19..30a4b89666 100644
> --- a/src/libcamera/software_isp/software_isp.cpp
> +++ b/src/libcamera/software_isp/software_isp.cpp
> @@ -71,10 +71,11 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
>   * \param[in] pipe The pipeline handler in use
>   * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline
>   * \param[out] ipaControls The IPA controls to update
> + * \param[out] metadataPlan The metadata plan to update
>   * handler
>   */
>  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
> -                        ControlInfoMap *ipaControls)
> +                        ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan)
>         : dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
>                    DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
>                    DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
> @@ -147,7 +148,8 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
>                          sensorInfo,
>                          sensor->controls(),
>                          ipaControls,
> -                        &ccmEnabled_);
> +                        &ccmEnabled_,
> +                        metadataPlan);
>         if (ret) {
>                 LOG(SoftwareIsp, Error) << "IPA init failed";
>                 debayer_.reset();
> -- 
> 2.51.1
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
index 7862465924..bd85f6b4bd 100644
--- a/include/libcamera/internal/software_isp/software_isp.h
+++ b/include/libcamera/internal/software_isp/software_isp.h
@@ -41,6 +41,7 @@  class DebayerCpu;
 class FrameBuffer;
 class PixelFormat;
 class Stream;
+class MetadataListPlan;
 struct StreamConfiguration;
 
 LOG_DECLARE_CATEGORY(SoftwareIsp)
@@ -49,7 +50,7 @@  class SoftwareIsp : public Object
 {
 public:
 	SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
-		    ControlInfoMap *ipaControls);
+		    ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan);
 	~SoftwareIsp();
 
 	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
diff --git a/include/libcamera/ipa/ipu3.mojom b/include/libcamera/ipa/ipu3.mojom
index d9a50b01db..f49b777973 100644
--- a/include/libcamera/ipa/ipu3.mojom
+++ b/include/libcamera/ipa/ipu3.mojom
@@ -20,7 +20,8 @@  interface IPAIPU3Interface {
 	init(libcamera.IPASettings settings,
 	     libcamera.IPACameraSensorInfo sensorInfo,
 	     libcamera.ControlInfoMap sensorControls)
-		=> (int32 ret, libcamera.ControlInfoMap ipaControls);
+		=> (int32 ret, libcamera.ControlInfoMap ipaControls,
+		    libcamera.MetadataListPlan metadata);
 	start() => (int32 ret);
 	stop();
 
diff --git a/include/libcamera/ipa/mali-c55.mojom b/include/libcamera/ipa/mali-c55.mojom
index 39b7f1f109..af1d083728 100644
--- a/include/libcamera/ipa/mali-c55.mojom
+++ b/include/libcamera/ipa/mali-c55.mojom
@@ -11,7 +11,8 @@  struct IPAConfigInfo {
 
 interface IPAMaliC55Interface {
 	init(libcamera.IPASettings settings, IPAConfigInfo configInfo)
-		=> (int32 ret, libcamera.ControlInfoMap ipaControls);
+		=> (int32 ret, libcamera.ControlInfoMap ipaControls,
+		    libcamera.MetadataListPlan metadataPlan);
 	start() => (int32 ret);
 	stop();
 
diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom
index 12b083e9d0..c32391911e 100644
--- a/include/libcamera/ipa/raspberrypi.mojom
+++ b/include/libcamera/ipa/raspberrypi.mojom
@@ -26,6 +26,7 @@  struct InitParams {
 struct InitResult {
 	SensorConfig sensorConfig;
 	libcamera.ControlInfoMap controlInfo;
+	libcamera.MetadataListPlan metadataPlan;
 };
 
 struct BufferIds {
diff --git a/include/libcamera/ipa/rkisp1.mojom b/include/libcamera/ipa/rkisp1.mojom
index 068e898848..015ba46036 100644
--- a/include/libcamera/ipa/rkisp1.mojom
+++ b/include/libcamera/ipa/rkisp1.mojom
@@ -19,7 +19,8 @@  interface IPARkISP1Interface {
 	     uint32 hwRevision, uint32 supportedBlocks,
 	     libcamera.IPACameraSensorInfo sensorInfo,
 	     libcamera.ControlInfoMap sensorControls)
-		=> (int32 ret, libcamera.ControlInfoMap ipaControls);
+		=> (int32 ret, libcamera.ControlInfoMap ipaControls,
+		    libcamera.MetadataListPlan metadataPlan);
 	start() => (int32 ret);
 	stop();
 
diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
index 77328c5fd5..360ed668b7 100644
--- a/include/libcamera/ipa/soft.mojom
+++ b/include/libcamera/ipa/soft.mojom
@@ -18,7 +18,8 @@  interface IPASoftInterface {
 	     libcamera.SharedFD fdParams,
 	     libcamera.IPACameraSensorInfo sensorInfo,
 	     libcamera.ControlInfoMap sensorControls)
-		=> (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled);
+		=> (int32 ret, libcamera.ControlInfoMap ipaControls, bool ccmEnabled,
+		    libcamera.MetadataListPlan metadataPlan);
 	start() => (int32 ret);
 	stop();
 	configure(IPAConfigInfo configInfo)
diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp
index b0d89541da..2d7b0482fe 100644
--- a/src/ipa/ipu3/algorithms/agc.cpp
+++ b/src/ipa/ipu3/algorithms/agc.cpp
@@ -82,6 +82,10 @@  int Agc::init(IPAContext &context, const YamlObject &tuningData)
 
 	context.ctrlMap.merge(controls());
 
+	context.metadataPlan.set(controls::AnalogueGain);
+	context.metadataPlan.set(controls::ExposureTime);
+	context.metadataPlan.set(controls::FrameDuration);
+
 	return 0;
 }
 
diff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp
index 55de05d9e3..499c7790f9 100644
--- a/src/ipa/ipu3/algorithms/awb.cpp
+++ b/src/ipa/ipu3/algorithms/awb.cpp
@@ -197,6 +197,18 @@  Awb::Awb()
 
 Awb::~Awb() = default;
 
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int Awb::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
+{
+	context.metadataPlan.set(controls::AwbEnable);
+	context.metadataPlan.set(controls::ColourGains);
+	context.metadataPlan.set(controls::ColourTemperature);
+
+	return 0;
+}
+
 /**
  * \copydoc libcamera::ipa::Algorithm::configure
  */
diff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h
index dbf69c9073..1d47bffa34 100644
--- a/src/ipa/ipu3/algorithms/awb.h
+++ b/src/ipa/ipu3/algorithms/awb.h
@@ -40,6 +40,7 @@  public:
 	Awb();
 	~Awb();
 
+	int init(IPAContext &context, const YamlObject &tuningData) override;
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context, const uint32_t frame,
 		     IPAFrameContext &frameContext,
diff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp
index 3b22f79176..f0b8b5dbea 100644
--- a/src/ipa/ipu3/ipa_context.cpp
+++ b/src/ipa/ipu3/ipa_context.cpp
@@ -54,6 +54,9 @@  namespace libcamera::ipa::ipu3 {
  *
  * \var IPAContext::ctrlMap
  * \brief A ControlInfoMap::Map of controls populated by the algorithms
+ *
+ * \var IPAContext::metadataPlan
+ * \brief A MetadataListPlan populated by the algorithms
  */
 
 /**
diff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h
index 97fcf06cd4..f4f45ef4d4 100644
--- a/src/ipa/ipu3/ipa_context.h
+++ b/src/ipa/ipu3/ipa_context.h
@@ -14,6 +14,7 @@ 
 
 #include <libcamera/controls.h>
 #include <libcamera/geometry.h>
+#include <libcamera/metadata_list_plan.h>
 
 #include <libipa/fc_queue.h>
 
@@ -95,6 +96,8 @@  struct IPAContext {
 	FCQueue<IPAFrameContext> frameContexts;
 
 	ControlInfoMap::Map ctrlMap;
+
+	MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
 };
 
 } /* namespace ipa::ipu3 */
diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp
index 1cae08bf25..8d8811f717 100644
--- a/src/ipa/ipu3/ipu3.cpp
+++ b/src/ipa/ipu3/ipu3.cpp
@@ -143,7 +143,8 @@  public:
 	int init(const IPASettings &settings,
 		 const IPACameraSensorInfo &sensorInfo,
 		 const ControlInfoMap &sensorControls,
-		 ControlInfoMap *ipaControls) override;
+		 ControlInfoMap *ipaControls,
+		 MetadataListPlan *metadataPlan) override;
 
 	int start() override;
 	void stop() override;
@@ -299,7 +300,8 @@  void IPAIPU3::updateControls(const IPACameraSensorInfo &sensorInfo,
 int IPAIPU3::init(const IPASettings &settings,
 		  const IPACameraSensorInfo &sensorInfo,
 		  const ControlInfoMap &sensorControls,
-		  ControlInfoMap *ipaControls)
+		  ControlInfoMap *ipaControls,
+		  MetadataListPlan *metadataPlan)
 {
 	camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
 	if (camHelper_ == nullptr) {
@@ -348,6 +350,8 @@  int IPAIPU3::init(const IPASettings &settings,
 	/* Initialize controls. */
 	updateControls(sensorInfo, sensorControls, ipaControls);
 
+	*metadataPlan = std::move(context_.metadataPlan);
+
 	return 0;
 }
 
diff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp
index f60fddac3f..97316ca35c 100644
--- a/src/ipa/mali-c55/algorithms/agc.cpp
+++ b/src/ipa/mali-c55/algorithms/agc.cpp
@@ -145,6 +145,11 @@  int Agc::init(IPAContext &context, const YamlObject &tuningData)
 	);
 	context.ctrlMap.merge(controls());
 
+	context.metadataPlan.set(controls::AnalogueGain);
+	context.metadataPlan.set(controls::ColourTemperature);
+	context.metadataPlan.set(controls::DigitalGain);
+	context.metadataPlan.set(controls::ExposureTime);
+
 	return 0;
 }
 
diff --git a/src/ipa/mali-c55/algorithms/awb.cpp b/src/ipa/mali-c55/algorithms/awb.cpp
index 3d546e5a85..1fd67a4f9f 100644
--- a/src/ipa/mali-c55/algorithms/awb.cpp
+++ b/src/ipa/mali-c55/algorithms/awb.cpp
@@ -29,6 +29,13 @@  Awb::Awb()
 {
 }
 
+int Awb::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
+{
+	context.metadataPlan.set(controls::ColourGains);
+
+	return 0;
+}
+
 int Awb::configure([[maybe_unused]] IPAContext &context,
 		   [[maybe_unused]] const IPACameraSensorInfo &configInfo)
 {
diff --git a/src/ipa/mali-c55/algorithms/awb.h b/src/ipa/mali-c55/algorithms/awb.h
index 2351d40555..6bdc88a193 100644
--- a/src/ipa/mali-c55/algorithms/awb.h
+++ b/src/ipa/mali-c55/algorithms/awb.h
@@ -18,6 +18,7 @@  public:
 	Awb();
 	~Awb() = default;
 
+	int init(IPAContext &context, const YamlObject &tuningData) override;
 	int configure(IPAContext &context,
 		      const IPACameraSensorInfo &configInfo) override;
 	void prepare(IPAContext &context, const uint32_t frame,
diff --git a/src/ipa/mali-c55/algorithms/blc.cpp b/src/ipa/mali-c55/algorithms/blc.cpp
index 2a54c86a91..d36c7ac66e 100644
--- a/src/ipa/mali-c55/algorithms/blc.cpp
+++ b/src/ipa/mali-c55/algorithms/blc.cpp
@@ -51,6 +51,8 @@  int BlackLevelCorrection::init([[maybe_unused]] IPAContext &context,
 
 	tuningParameters_ = true;
 
+	context.metadataPlan.set(controls::SensorBlackLevels);
+
 	LOG(MaliC55Blc, Debug)
 		<< "Black levels: 00 " << offset00 << ", 01 " << offset01
 		<< ", 10 " << offset10 << ", 11 " << offset11;
diff --git a/src/ipa/mali-c55/ipa_context.h b/src/ipa/mali-c55/ipa_context.h
index 13885eb83b..55aef1d100 100644
--- a/src/ipa/mali-c55/ipa_context.h
+++ b/src/ipa/mali-c55/ipa_context.h
@@ -9,6 +9,7 @@ 
 
 #include <libcamera/base/utils.h>
 #include <libcamera/controls.h>
+#include <libcamera/metadata_list_plan.h>
 
 #include "libcamera/internal/bayer_format.h"
 
@@ -83,6 +84,8 @@  struct IPAContext {
 	FCQueue<IPAFrameContext> frameContexts;
 
 	ControlInfoMap::Map ctrlMap;
+
+	MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
 };
 
 } /* namespace ipa::mali_c55 */
diff --git a/src/ipa/mali-c55/mali-c55.cpp b/src/ipa/mali-c55/mali-c55.cpp
index 7d45e7310a..10b61b4fb3 100644
--- a/src/ipa/mali-c55/mali-c55.cpp
+++ b/src/ipa/mali-c55/mali-c55.cpp
@@ -46,7 +46,7 @@  public:
 	IPAMaliC55();
 
 	int init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,
-		 ControlInfoMap *ipaControls) override;
+		 ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan) override;
 	int start() override;
 	void stop() override;
 	int configure(const IPAConfigInfo &ipaConfig, uint8_t bayerOrder,
@@ -96,7 +96,7 @@  std::string IPAMaliC55::logPrefix() const
 }
 
 int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig,
-		     ControlInfoMap *ipaControls)
+		     ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan)
 {
 	camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
 	if (!camHelper_) {
@@ -131,6 +131,8 @@  int IPAMaliC55::init(const IPASettings &settings, const IPAConfigInfo &ipaConfig
 
 	updateControls(ipaConfig.sensorInfo, ipaConfig.sensorControls, ipaControls);
 
+	*metadataPlan = std::move(context_.metadataPlan);
+
 	return 0;
 }
 
diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp
index f5a3c917cb..cbc7311cd5 100644
--- a/src/ipa/rkisp1/algorithms/agc.cpp
+++ b/src/ipa/rkisp1/algorithms/agc.cpp
@@ -160,6 +160,16 @@  int Agc::init(IPAContext &context, const YamlObject &tuningData)
 	context.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);
 	context.ctrlMap.merge(controls());
 
+	context.metadataPlan.set(controls::AeConstraintMode);
+	context.metadataPlan.set(controls::AeExposureMode);
+	context.metadataPlan.set(controls::AeMeteringMode);
+	context.metadataPlan.set(controls::AnalogueGain);
+	context.metadataPlan.set(controls::AnalogueGainMode);
+	context.metadataPlan.set(controls::ExposureTime);
+	context.metadataPlan.set(controls::ExposureTimeMode);
+	context.metadataPlan.set(controls::ExposureValue);
+	context.metadataPlan.set(controls::FrameDuration);
+
 	return 0;
 }
 
diff --git a/src/ipa/rkisp1/algorithms/awb.cpp b/src/ipa/rkisp1/algorithms/awb.cpp
index 399fb51be4..8de7ea3951 100644
--- a/src/ipa/rkisp1/algorithms/awb.cpp
+++ b/src/ipa/rkisp1/algorithms/awb.cpp
@@ -117,6 +117,10 @@  int Awb::init(IPAContext &context, const YamlObject &tuningData)
 	const auto &src = awbAlgo_->controls();
 	cmap.insert(src.begin(), src.end());
 
+	context.metadataPlan.set(controls::AwbEnable);
+	context.metadataPlan.set(controls::ColourGains);
+	context.metadataPlan.set(controls::ColourTemperature);
+
 	return 0;
 }
 
diff --git a/src/ipa/rkisp1/algorithms/blc.cpp b/src/ipa/rkisp1/algorithms/blc.cpp
index 32fc44ffff..03071360b3 100644
--- a/src/ipa/rkisp1/algorithms/blc.cpp
+++ b/src/ipa/rkisp1/algorithms/blc.cpp
@@ -103,6 +103,8 @@  int BlackLevelCorrection::init(IPAContext &context, const YamlObject &tuningData
 		<< ", green (blue) " << blackLevelGreenB_
 		<< ", blue " << blackLevelBlue_;
 
+	context.metadataPlan.set(controls::SensorBlackLevels);
+
 	return 0;
 }
 
diff --git a/src/ipa/rkisp1/algorithms/ccm.cpp b/src/ipa/rkisp1/algorithms/ccm.cpp
index de2b6fe775..3115d3effa 100644
--- a/src/ipa/rkisp1/algorithms/ccm.cpp
+++ b/src/ipa/rkisp1/algorithms/ccm.cpp
@@ -66,6 +66,8 @@  int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
 		offsets_.setData({ { 0, Matrix<int16_t, 3, 1>({ 0, 0, 0 }) } });
 	}
 
+	context.metadataPlan.set(controls::ColourCorrectionMatrix);
+
 	return 0;
 }
 
diff --git a/src/ipa/rkisp1/algorithms/goc.cpp b/src/ipa/rkisp1/algorithms/goc.cpp
index a0e7030fe5..46a4c2c364 100644
--- a/src/ipa/rkisp1/algorithms/goc.cpp
+++ b/src/ipa/rkisp1/algorithms/goc.cpp
@@ -60,6 +60,8 @@  int GammaOutCorrection::init(IPAContext &context, const YamlObject &tuningData)
 	defaultGamma_ = tuningData["gamma"].get<double>(kDefaultGamma);
 	context.ctrlMap[&controls::Gamma] = ControlInfo(0.1f, 10.0f, defaultGamma_);
 
+	context.metadataPlan.set(controls::Gamma);
+
 	return 0;
 }
 
diff --git a/src/ipa/rkisp1/algorithms/lux.cpp b/src/ipa/rkisp1/algorithms/lux.cpp
index e8da698100..966c711375 100644
--- a/src/ipa/rkisp1/algorithms/lux.cpp
+++ b/src/ipa/rkisp1/algorithms/lux.cpp
@@ -43,7 +43,13 @@  Lux::Lux()
  */
 int Lux::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData)
 {
-	return lux_.parseTuningData(tuningData);
+	int ret = lux_.parseTuningData(tuningData);
+	if (ret)
+		return ret;
+
+	context.metadataPlan.set(controls::Lux);
+
+	return 0;
 }
 
 /**
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index f85a130d9c..caf7d9d448 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -230,6 +230,7 @@  struct IPAContext {
 	FCQueue<IPAFrameContext> frameContexts;
 
 	ControlInfoMap::Map ctrlMap;
+	MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
 
 	DebugMetadata debugMetadata;
 
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index fa22bfc349..9815b83a03 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -55,7 +55,8 @@  public:
 		 uint32_t supportedBlocks,
 		 const IPACameraSensorInfo &sensorInfo,
 		 const ControlInfoMap &sensorControls,
-		 ControlInfoMap *ipaControls) override;
+		 ControlInfoMap *ipaControls,
+		 MetadataListPlan *metadataPlan) override;
 	int start() override;
 	void stop() override;
 
@@ -139,7 +140,8 @@  int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
 		    uint32_t supportedBlocks,
 		    const IPACameraSensorInfo &sensorInfo,
 		    const ControlInfoMap &sensorControls,
-		    ControlInfoMap *ipaControls)
+		    ControlInfoMap *ipaControls,
+		    MetadataListPlan *metadataPlan)
 {
 	/* \todo Add support for other revisions */
 	switch (hwRevision) {
@@ -209,6 +211,8 @@  int IPARkISP1::init(const IPASettings &settings, unsigned int hwRevision,
 	/* Initialize controls. */
 	updateControls(sensorInfo, sensorControls, ipaControls);
 
+	*metadataPlan = std::move(context_.metadataPlan);
+
 	return 0;
 }
 
diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp
index 8dfe35cc32..15e65f9315 100644
--- a/src/ipa/rpi/common/ipa_base.cpp
+++ b/src/ipa/rpi/common/ipa_base.cpp
@@ -182,6 +182,40 @@  int32_t IpaBase::init(const IPASettings &settings, const InitParams &params, Ini
 
 	result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls);
 
+	// TODO: only set those that can be reported by configured algorithms?
+	// TODO: move this somewhere else?
+	result->metadataPlan.set(controls::AeConstraintMode);
+	result->metadataPlan.set(controls::AeExposureMode);
+	result->metadataPlan.set(controls::AeMeteringMode);
+	result->metadataPlan.set(controls::AeState);
+	result->metadataPlan.set(controls::AfPauseState);
+	result->metadataPlan.set(controls::AfState);
+	result->metadataPlan.set(controls::AnalogueGain);
+	result->metadataPlan.set(controls::AnalogueGainMode);
+	result->metadataPlan.set(controls::AwbEnable);
+	result->metadataPlan.set(controls::AwbMode);
+	result->metadataPlan.set(controls::Brightness);
+	result->metadataPlan.set(controls::ColourCorrectionMatrix);
+	result->metadataPlan.set(controls::ColourGains);
+	result->metadataPlan.set(controls::ColourTemperature);
+	result->metadataPlan.set(controls::Contrast);
+	result->metadataPlan.set(controls::DigitalGain);
+	result->metadataPlan.set(controls::ExposureTime);
+	result->metadataPlan.set(controls::ExposureTimeMode);
+	result->metadataPlan.set(controls::ExposureValue);
+	result->metadataPlan.set(controls::FocusFoM);
+	result->metadataPlan.set(controls::FrameDuration);
+	result->metadataPlan.set(controls::FrameDurationLimits);
+	result->metadataPlan.set(controls::HdrChannel);
+	result->metadataPlan.set(controls::HdrMode);
+	result->metadataPlan.set(controls::LensPosition);
+	result->metadataPlan.set(controls::Lux);
+	result->metadataPlan.set(controls::Saturation);
+	result->metadataPlan.set(controls::SensorBlackLevels);
+	result->metadataPlan.set(controls::SensorTemperature);
+	result->metadataPlan.set(controls::Sharpness);
+	result->metadataPlan.set(controls::draft::NoiseReductionMode);
+
 	return platformInit(params, result);
 }
 
diff --git a/src/ipa/rpi/pisp/pisp.cpp b/src/ipa/rpi/pisp/pisp.cpp
index ec7593ffc9..04e7731d22 100644
--- a/src/ipa/rpi/pisp/pisp.cpp
+++ b/src/ipa/rpi/pisp/pisp.cpp
@@ -291,8 +291,7 @@  private:
 	HdrStatus lastStitchHdrStatus_;
 };
 
-int32_t IpaPiSP::platformInit(const InitParams &params,
-			      [[maybe_unused]] InitResult *result)
+int32_t IpaPiSP::platformInit(const InitParams &params, InitResult *result)
 {
 	const std::string &target = controller_.getTarget();
 	if (target != "pisp") {
@@ -325,6 +324,8 @@  int32_t IpaPiSP::platformInit(const InitParams &params,
 
 	setDefaultConfig();
 
+	result->metadataPlan.set(controls::rpi::PispStatsOutput, sizeof(pisp_statistics));
+
 	return 0;
 }
 
diff --git a/src/ipa/rpi/vc4/vc4.cpp b/src/ipa/rpi/vc4/vc4.cpp
index 2b205b2861..3014925eaf 100644
--- a/src/ipa/rpi/vc4/vc4.cpp
+++ b/src/ipa/rpi/vc4/vc4.cpp
@@ -91,7 +91,7 @@  private:
 	AwbStatus lastAwbStatus_;
 };
 
-int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, [[maybe_unused]] InitResult *result)
+int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, InitResult *result)
 {
 	const std::string &target = controller_.getTarget();
 
@@ -102,6 +102,8 @@  int32_t IpaVc4::platformInit([[maybe_unused]] const InitParams &params, [[maybe_
 		return -EINVAL;
 	}
 
+	result->metadataPlan.set(controls::rpi::Bcm2835StatsOutput, sizeof(bcm2835_isp_stats));
+
 	return 0;
 }
 
diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp
index 189de770c6..c4e52328f0 100644
--- a/src/ipa/simple/algorithms/agc.cpp
+++ b/src/ipa/simple/algorithms/agc.cpp
@@ -41,6 +41,14 @@  Agc::Agc()
 {
 }
 
+int Agc::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
+{
+	context.metadataPlan.set(controls::AnalogueGain);
+	context.metadataPlan.set(controls::ExposureTime);
+
+	return 0;
+}
+
 void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV)
 {
 	/*
diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h
index 112d9f5a19..00e70ea705 100644
--- a/src/ipa/simple/algorithms/agc.h
+++ b/src/ipa/simple/algorithms/agc.h
@@ -19,6 +19,7 @@  public:
 	Agc();
 	~Agc() = default;
 
+	int init(IPAContext &context, const YamlObject &tuningData) override;
 	void process(IPAContext &context, const uint32_t frame,
 		     IPAFrameContext &frameContext,
 		     const SwIspStats *stats,
diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp
index cf78e98009..fe605117ab 100644
--- a/src/ipa/simple/algorithms/awb.cpp
+++ b/src/ipa/simple/algorithms/awb.cpp
@@ -26,6 +26,14 @@  LOG_DEFINE_CATEGORY(IPASoftAwb)
 
 namespace ipa::soft::algorithms {
 
+int Awb::init(IPAContext &context, [[maybe_unused]] const YamlObject &tuningData)
+{
+	context.metadataPlan.set(controls::ColourGains);
+	context.metadataPlan.set(controls::ColourTemperature);
+
+	return 0;
+}
+
 int Awb::configure(IPAContext &context,
 		   [[maybe_unused]] const IPAConfigInfo &configInfo)
 {
diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h
index ad993f39c1..b8ae63dcb1 100644
--- a/src/ipa/simple/algorithms/awb.h
+++ b/src/ipa/simple/algorithms/awb.h
@@ -19,6 +19,7 @@  public:
 	Awb() = default;
 	~Awb() = default;
 
+	int init(IPAContext &context, const YamlObject &tuningData) override;
 	int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
 	void prepare(IPAContext &context,
 		     const uint32_t frame,
diff --git a/src/ipa/simple/algorithms/blc.cpp b/src/ipa/simple/algorithms/blc.cpp
index 370385afc6..bcb67b832a 100644
--- a/src/ipa/simple/algorithms/blc.cpp
+++ b/src/ipa/simple/algorithms/blc.cpp
@@ -34,6 +34,9 @@  int BlackLevel::init([[maybe_unused]] IPAContext &context,
 		 */
 		definedLevel_ = blackLevel.value() >> 8;
 	}
+
+	context.metadataPlan.set(controls::SensorBlackLevels);
+
 	return 0;
 }
 
diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp
index 0a98406c1a..b1758ff39b 100644
--- a/src/ipa/simple/algorithms/ccm.cpp
+++ b/src/ipa/simple/algorithms/ccm.cpp
@@ -39,6 +39,9 @@  int Ccm::init([[maybe_unused]] IPAContext &context, const YamlObject &tuningData
 	context.ccmEnabled = true;
 	context.ctrlMap[&controls::Saturation] = ControlInfo(0.0f, 2.0f, 1.0f);
 
+	context.metadataPlan.set(controls::ColourCorrectionMatrix);
+	context.metadataPlan.set(controls::Saturation);
+
 	return 0;
 }
 
diff --git a/src/ipa/simple/algorithms/lut.cpp b/src/ipa/simple/algorithms/lut.cpp
index d1d5f72712..5a7ffbd8fa 100644
--- a/src/ipa/simple/algorithms/lut.cpp
+++ b/src/ipa/simple/algorithms/lut.cpp
@@ -28,6 +28,9 @@  int Lut::init(IPAContext &context,
 	      [[maybe_unused]] const YamlObject &tuningData)
 {
 	context.ctrlMap[&controls::Contrast] = ControlInfo(0.0f, 2.0f, 1.0f);
+
+	context.metadataPlan.set(controls::Contrast);
+
 	return 0;
 }
 
diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h
index c3081e3069..d76c0f79ee 100644
--- a/src/ipa/simple/ipa_context.h
+++ b/src/ipa/simple/ipa_context.h
@@ -12,6 +12,7 @@ 
 #include <stdint.h>
 
 #include <libcamera/controls.h>
+#include <libcamera/metadata_list_plan.h>
 
 #include "libcamera/internal/matrix.h"
 #include "libcamera/internal/vector.h"
@@ -102,6 +103,7 @@  struct IPAContext {
 	IPAActiveState activeState;
 	FCQueue<IPAFrameContext> frameContexts;
 	ControlInfoMap::Map ctrlMap;
+	MetadataListPlan metadataPlan; // TODO: only needed during init(), how could be removed?
 	bool ccmEnabled = false;
 };
 
diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
index b147aca2e3..9d698a27b3 100644
--- a/src/ipa/simple/soft_simple.cpp
+++ b/src/ipa/simple/soft_simple.cpp
@@ -56,7 +56,8 @@  public:
 		 const IPACameraSensorInfo &sensorInfo,
 		 const ControlInfoMap &sensorControls,
 		 ControlInfoMap *ipaControls,
-		 bool *ccmEnabled) override;
+		 bool *ccmEnabled,
+		 MetadataListPlan *metadataPlan) override;
 	int configure(const IPAConfigInfo &configInfo) override;
 
 	int start() override;
@@ -96,7 +97,8 @@  int IPASoftSimple::init(const IPASettings &settings,
 			const IPACameraSensorInfo &sensorInfo,
 			const ControlInfoMap &sensorControls,
 			ControlInfoMap *ipaControls,
-			bool *ccmEnabled)
+			bool *ccmEnabled,
+			MetadataListPlan *metadataPlan)
 {
 	camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
 	if (!camHelper_) {
@@ -190,6 +192,8 @@  int IPASoftSimple::init(const IPASettings &settings,
 		return -EINVAL;
 	}
 
+	*metadataPlan = std::move(context_.metadataPlan);
+
 	return 0;
 }
 
diff --git a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
index de09431cb9..fbad880b23 100644
--- a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
+++ b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp
@@ -166,6 +166,8 @@  int ISICameraData::init()
 
 	properties_ = sensor_->properties();
 
+	metadataPlan_.set(controls::SensorTimestamp);
+
 	return 0;
 }
 
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index d6b7edcb5a..13dbdb6268 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -1082,6 +1082,11 @@  int PipelineHandlerIPU3::registerCameras()
 		if (ret)
 			continue;
 
+		data->metadataPlan_.set(controls::draft::PipelineDepth);
+		data->metadataPlan_.set(controls::draft::TestPatternMode);
+		data->metadataPlan_.set(controls::ScalerCrop);
+		data->metadataPlan_.set(controls::SensorTimestamp);
+
 		const CameraSensorProperties::SensorDelays &delays = cio2->sensor()->sensorDelays();
 		std::unordered_map<uint32_t, DelayedControls::ControlParams> params = {
 			{ V4L2_CID_ANALOGUE_GAIN, { delays.gainDelay, false } },
@@ -1191,7 +1196,7 @@  int IPU3CameraData::loadIPA()
 		ipa_->configurationFile(sensor->model() + ".yaml", "uncalibrated.yaml");
 
 	ret = ipa_->init(IPASettings{ ipaTuningFile, sensor->model() },
-			 sensorInfo, sensor->controls(), &ipaControls_);
+			 sensorInfo, sensor->controls(), &ipaControls_, &metadataPlan_);
 	if (ret) {
 		LOG(IPU3, Error) << "Failed to initialise the IPU3 IPA";
 		return ret;
diff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
index 38bdc6138e..938c5b2890 100644
--- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp
+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp
@@ -402,7 +402,7 @@  int MaliC55CameraData::loadIPA()
 
 	ControlInfoMap ipaControls;
 	ret = ipa_->init({ ipaTuningFile, sensor_->model() }, ipaConfig,
-			 &ipaControls);
+			 &ipaControls, &metadataPlan_);
 	if (ret) {
 		LOG(MaliC55, Error) << "Failed to initialise the Mali-C55 IPA";
 		return ret;
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index ecd1383153..002a44f304 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -406,7 +406,7 @@  int RkISP1CameraData::loadIPA(unsigned int hwRevision, uint32_t supportedBlocks)
 
 	ret = ipa_->init({ ipaTuningFile, sensor_->model() }, hwRevision,
 			 supportedBlocks, sensorInfo, sensor_->controls(),
-			 &ipaControls_);
+			 &ipaControls_, &metadataPlan_);
 	if (ret < 0) {
 		LOG(RkISP1, Error) << "IPA initialization failure";
 		return ret;
@@ -1377,6 +1377,8 @@  int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
 
 	updateControls(data.get());
 
+	data->metadataPlan_.set(controls::SensorTimestamp);
+
 	std::set<Stream *> streams{
 		&data->mainPathStream_,
 		&data->selfPathStream_,
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
index c209aa5963..87ce290225 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -589,6 +589,9 @@  int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
 
 	data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
 
+	/* Update `rpi::ScalerCrops` size for the corrent configuration. */
+	data->metadataPlan_.set(controls::rpi::ScalerCrops, config->size());
+
 	/* Setup the Video Mux/Bridge entities. */
 	for (auto &[device, link] : data->bridgeDevices_) {
 		/*
@@ -835,6 +838,11 @@  int PipelineHandlerBase::registerCamera(std::unique_ptr<RPi::CameraData> &camera
 	/* Initialize the camera properties. */
 	data->properties_ = data->sensor_->properties();
 
+	data->metadataPlan_ = std::move(result.metadataPlan);
+	data->metadataPlan_.set(controls::SensorTimestamp);
+	data->metadataPlan_.set(controls::FrameWallClock);
+	data->metadataPlan_.set(controls::ScalerCrop);
+
 	/*
 	 * The V4L2_CID_NOTIFY_GAINS control, if present, is used to inform the
 	 * sensor of the colour gains. It is defined to be a linear gain where
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 91715b7f8a..312f35ad15 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -602,7 +602,7 @@  int SimpleCameraData::init()
 	 * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
 	 */
 	if (!converter_ && pipe->swIspEnabled()) {
-		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get(), &controlInfo_);
+		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get(), &controlInfo_, &metadataPlan_);
 		if (!swIsp_->isValid()) {
 			LOG(SimplePipeline, Warning)
 				<< "Failed to create software ISP, disabling software debayering";
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 4b5816dfdd..59fb4bd5c9 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -614,6 +614,8 @@  int UVCCameraData::init(MediaDevice *media)
 
 	controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);
 
+	metadataPlan_.set(controls::SensorTimestamp);
+
 	/*
 	 * Close to allow camera to go into runtime-suspend, video_ will be
 	 * re-opened from acquireDevice() and validate().
diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
index 5022101505..1b6880a5dc 100644
--- a/src/libcamera/pipeline/vimc/vimc.cpp
+++ b/src/libcamera/pipeline/vimc/vimc.cpp
@@ -593,6 +593,8 @@  int VimcCameraData::init()
 
 	controlInfo_ = ControlInfoMap(std::move(ctrls), controls::controls);
 
+	metadataPlan_.set(controls::SensorTimestamp);
+
 	/* Initialize the camera properties. */
 	properties_ = sensor_->properties();
 
diff --git a/src/libcamera/pipeline/virtual/config_parser.cpp b/src/libcamera/pipeline/virtual/config_parser.cpp
index 1d3d9ba87e..608bbd469f 100644
--- a/src/libcamera/pipeline/virtual/config_parser.cpp
+++ b/src/libcamera/pipeline/virtual/config_parser.cpp
@@ -65,6 +65,9 @@  ConfigParser::parseConfigFile(File &file, PipelineHandler *pipe)
 		controls[&controls::draft::FaceDetectMode] = ControlInfo(supportedFaceDetectModes);
 
 		data->controlInfo_ = ControlInfoMap(std::move(controls), controls::controls);
+
+		data->metadataPlan_.set(controls::SensorTimestamp);
+
 		configurations.push_back(std::move(data));
 	}
 
diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
index fdadf79e19..30a4b89666 100644
--- a/src/libcamera/software_isp/software_isp.cpp
+++ b/src/libcamera/software_isp/software_isp.cpp
@@ -71,10 +71,11 @@  LOG_DEFINE_CATEGORY(SoftwareIsp)
  * \param[in] pipe The pipeline handler in use
  * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline
  * \param[out] ipaControls The IPA controls to update
+ * \param[out] metadataPlan The metadata plan to update
  * handler
  */
 SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
-			 ControlInfoMap *ipaControls)
+			 ControlInfoMap *ipaControls, MetadataListPlan *metadataPlan)
 	: dmaHeap_(DmaBufAllocator::DmaBufAllocatorFlag::CmaHeap |
 		   DmaBufAllocator::DmaBufAllocatorFlag::SystemHeap |
 		   DmaBufAllocator::DmaBufAllocatorFlag::UDmaBuf)
@@ -147,7 +148,8 @@  SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor,
 			 sensorInfo,
 			 sensor->controls(),
 			 ipaControls,
-			 &ccmEnabled_);
+			 &ccmEnabled_,
+			 metadataPlan);
 	if (ret) {
 		LOG(SoftwareIsp, Error) << "IPA init failed";
 		debayer_.reset();