[1/1] libcamera: Camera: Add signals for completion of metadata as a partial result
diff mbox series

Message ID 20240912045703.3446748-2-chenghaoyang@google.com
State New
Headers show
Series
  • Add metadataCompleted Signal in Camera
Related show

Commit Message

Cheng-Hao Yang Sept. 12, 2024, 4:52 a.m. UTC
From: Han-Lin Chen <hanlinchen@chromium.org>

Allows pipeline handler to signal metadata completion by adding the
following signals to Camera:

Signal<Request *, const ControlList &> metadataCompleted;

Together with the bufferCompleted signal, the pipeline handler is allowed to
return buffers and partial metadata at any stage of processing.

Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
---
 include/libcamera/camera.h                    |  1 +
 include/libcamera/internal/pipeline_handler.h |  1 +
 include/libcamera/request.h                   |  5 +++
 src/libcamera/camera.cpp                      |  6 +++
 src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
 src/libcamera/request.cpp                     | 20 ++++++++++
 6 files changed, 70 insertions(+)

Comments

Cheng-Hao Yang Nov. 13, 2024, 7 a.m. UTC | #1
Gentle ping: May we review this patch recently, so that I can continue
uploading the corresponding patches in Android Adapter?
Thanks :)

BR,
Harvey

On Thu, Sep 12, 2024 at 12:57 PM Harvey Yang <chenghaoyang@chromium.org> wrote:
>
> From: Han-Lin Chen <hanlinchen@chromium.org>
>
> Allows pipeline handler to signal metadata completion by adding the
> following signals to Camera:
>
> Signal<Request *, const ControlList &> metadataCompleted;
>
> Together with the bufferCompleted signal, the pipeline handler is allowed to
> return buffers and partial metadata at any stage of processing.
>
> Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> ---
>  include/libcamera/camera.h                    |  1 +
>  include/libcamera/internal/pipeline_handler.h |  1 +
>  include/libcamera/request.h                   |  5 +++
>  src/libcamera/camera.cpp                      |  6 +++
>  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
>  src/libcamera/request.cpp                     | 20 ++++++++++
>  6 files changed, 70 insertions(+)
>
> diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> index 94cee7bd..08c5c58c 100644
> --- a/include/libcamera/camera.h
> +++ b/include/libcamera/camera.h
> @@ -122,6 +122,7 @@ public:
>
>         const std::string &id() const;
>
> +       Signal<Request *, const ControlList &> metadataCompleted;
>         Signal<Request *, FrameBuffer *> bufferCompleted;
>         Signal<Request *> requestCompleted;
>         Signal<> disconnected;
> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> index 0d380803..1af63a23 100644
> --- a/include/libcamera/internal/pipeline_handler.h
> +++ b/include/libcamera/internal/pipeline_handler.h
> @@ -58,6 +58,7 @@ public:
>         void registerRequest(Request *request);
>         void queueRequest(Request *request);
>
> +       void completeMetadata(Request *request, const ControlList &metadata);
>         bool completeBuffer(Request *request, FrameBuffer *buffer);
>         void completeRequest(Request *request);
>
> diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> index e214a9d1..0200f4a1 100644
> --- a/include/libcamera/request.h
> +++ b/include/libcamera/request.h
> @@ -12,6 +12,7 @@
>  #include <ostream>
>  #include <stdint.h>
>  #include <string>
> +#include <unordered_set>
>
>  #include <libcamera/base/class.h>
>  #include <libcamera/base/signal.h>
> @@ -64,6 +65,8 @@ public:
>
>         std::string toString() const;
>
> +       ControlList addCompletedMetadata(const ControlList &metadata);
> +
>  private:
>         LIBCAMERA_DISABLE_COPY(Request)
>
> @@ -73,6 +76,8 @@ private:
>
>         const uint64_t cookie_;
>         Status status_;
> +
> +       std::unordered_set<unsigned int> completedMetadata_;
>  };
>
>  std::ostream &operator<<(std::ostream &out, const Request &r);
> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> index a86f552a..5ffae23e 100644
> --- a/src/libcamera/camera.cpp
> +++ b/src/libcamera/camera.cpp
> @@ -892,6 +892,12 @@ const std::string &Camera::id() const
>   * completed
>   */
>
> +/**
> + * \var Camera::metadataCompleted
> + * \brief Signal emitted when some metadata for a request is completed as a
> + * partial result
> + */
> +
>  /**
>   * \var Camera::requestCompleted
>   * \brief Signal emitted when a request queued to the camera has completed
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index e5940469..5d2999cb 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
>         return request->_d()->completeBuffer(buffer);
>  }
>
> +/**
> + * \brief Complete part of metadata for a request
> + * \param[in] request The request the buffer belongs to
> + * \param[in] metadata The partial metadata that has completed
> + *
> + * This function could be called by pipeline handlers to signal completion of
> + * the \a metadata part of the \a request. It notifies applications of metadata
> + * completion.
> + *
> + * \context This function shall be called from the CameraManager thread.
> + */
> +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> +{
> +       const ControlList &validMetadata = request->addCompletedMetadata(metadata);
> +       if (!validMetadata.empty()) {
> +               request->metadata().merge(validMetadata);
> +
> +               Camera *camera = request->_d()->camera();
> +               camera->metadataCompleted.emit(request, validMetadata);
> +       }
> +}
> +
>  /**
>   * \brief Signal request completion
>   * \param[in] request The request that has completed
> @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
>
>         Camera::Private *data = camera->_d();
>
> +       /*
> +        * Collect metadata which is not yet completed by the Camera, and
> +        * create one partial result to cover the missing metadata before
> +        * completing the whole request. This guarantees the aggregation of
> +        * metadata in completed partial results equals to the global metadata
> +        * in the request.
> +        *
> +        * \todo: Forbid merging metadata into request.metadata() directly and
> +        * force calling completeMetadata() to report metadata.
> +        */
> +       const ControlList &validMetadata = request->addCompletedMetadata(
> +               request->metadata());
> +       if (!validMetadata.empty())
> +               camera->metadataCompleted.emit(request, validMetadata);
> +
>         while (!data->queuedRequests_.empty()) {
>                 Request *req = data->queuedRequests_.front();
>                 if (req->status() == Request::RequestPending)
> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> index 8c56ed30..3ce23a8e 100644
> --- a/src/libcamera/request.cpp
> +++ b/src/libcamera/request.cpp
> @@ -598,6 +598,26 @@ std::string Request::toString() const
>         return ss.str();
>  }
>
> +/**
> + * \brief Add completed metadata, as a partial result
> + * \param[in] metadata The metadata completed
> + *
> + * Request will record the entries that has been sent to the application, to
> + * prevent duplicated controls.
> + *
> + * \return ControlList that hasn't been completed before
> + */
> +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> +{
> +       ControlList resultMetadata;
> +       for (auto &[id, value] : metadata) {
> +               if (!completedMetadata_.count(id))
> +                       resultMetadata.set(id, value);
> +       }
> +
> +       return resultMetadata;
> +}
> +
>  /**
>   * \brief Insert a text representation of a Request into an output stream
>   * \param[in] out The output stream
> --
> 2.46.0.598.g6f2099f65c-goog
>
Jacopo Mondi Nov. 13, 2024, 10:46 a.m. UTC | #2
Hi Harvey,
   sorry for the late feedback

On Thu, Sep 12, 2024 at 04:52:48AM +0000, Harvey Yang wrote:
> From: Han-Lin Chen <hanlinchen@chromium.org>
>
> Allows pipeline handler to signal metadata completion by adding the
> following signals to Camera:
>
> Signal<Request *, const ControlList &> metadataCompleted;

Bikeshedding on the name a bit, as this is allowed to be called
multiple times, with partial metadata, I would call it
'metadataAvailable'.

>
> Together with the bufferCompleted signal, the pipeline handler is allowed to
> return buffers and partial metadata at any stage of processing.
>
> Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> ---
>  include/libcamera/camera.h                    |  1 +
>  include/libcamera/internal/pipeline_handler.h |  1 +
>  include/libcamera/request.h                   |  5 +++
>  src/libcamera/camera.cpp                      |  6 +++
>  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
>  src/libcamera/request.cpp                     | 20 ++++++++++
>  6 files changed, 70 insertions(+)
>
> diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> index 94cee7bd..08c5c58c 100644
> --- a/include/libcamera/camera.h
> +++ b/include/libcamera/camera.h
> @@ -122,6 +122,7 @@ public:
>
>  	const std::string &id() const;
>
> +	Signal<Request *, const ControlList &> metadataCompleted;
>  	Signal<Request *, FrameBuffer *> bufferCompleted;
>  	Signal<Request *> requestCompleted;
>  	Signal<> disconnected;
> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> index 0d380803..1af63a23 100644
> --- a/include/libcamera/internal/pipeline_handler.h
> +++ b/include/libcamera/internal/pipeline_handler.h
> @@ -58,6 +58,7 @@ public:
>  	void registerRequest(Request *request);
>  	void queueRequest(Request *request);
>
> +	void completeMetadata(Request *request, const ControlList &metadata);
>  	bool completeBuffer(Request *request, FrameBuffer *buffer);
>  	void completeRequest(Request *request);
>
> diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> index e214a9d1..0200f4a1 100644
> --- a/include/libcamera/request.h
> +++ b/include/libcamera/request.h
> @@ -12,6 +12,7 @@
>  #include <ostream>
>  #include <stdint.h>
>  #include <string>
> +#include <unordered_set>
>
>  #include <libcamera/base/class.h>
>  #include <libcamera/base/signal.h>
> @@ -64,6 +65,8 @@ public:
>
>  	std::string toString() const;
>
> +	ControlList addCompletedMetadata(const ControlList &metadata);
> +
>  private:
>  	LIBCAMERA_DISABLE_COPY(Request)
>
> @@ -73,6 +76,8 @@ private:
>
>  	const uint64_t cookie_;
>  	Status status_;
> +
> +	std::unordered_set<unsigned int> completedMetadata_;
>  };
>
>  std::ostream &operator<<(std::ostream &out, const Request &r);
> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> index a86f552a..5ffae23e 100644
> --- a/src/libcamera/camera.cpp
> +++ b/src/libcamera/camera.cpp
> @@ -892,6 +892,12 @@ const std::string &Camera::id() const
>   * completed
>   */
>
> +/**
> + * \var Camera::metadataCompleted
> + * \brief Signal emitted when some metadata for a request is completed as a
> + * partial result

Currently, we return metadata as part of a Request signalled with
Camera::requestCompleted. Existing applications do not handle the new
signal and I don't think we should make it mandatory to do so.
According to the implementation of this patch, the same application
running on a pipeline handler that calls
PipelineHandler::completeMetadata and on a pipeline handler that
doesn't will receive different sets of metadata.

- If a pipeline handler calls PipelineHandler::completeMetadata() it
  will send metadata in chunks and only the ones not previously
  signalled with be part of Request::metadata().

- If a pipeline handler doesn't call PipelineHandler::completeMetadata()
  the full metadata buffer will be in Request::metadata().

How does an application know what to expect ?

Imo we should advertise if the camera supports partial metadata or not
(maybe through a property). If it does applications should opt-in to
receive partial metadata. This could even be done per-configuration by
adding a field to CameraConfiguration ?

If applications enable early metdata completion they agree to handle
the new signal, and the behaviour will be the one implemented in this
patch. If they do not enable early metadata completion then
PipelineHandler::completeMetadata() should simply merge the partial
metadata in Request::metadata() and not emit the new signal but return
the full metadata buffer as part of the Request in requestCompleted,
as they currently do so that pipelines that use early completion and
pipelines that do not will behave identically from an application
perspective.

What do you think ?

> + */
> +
>  /**
>   * \var Camera::requestCompleted
>   * \brief Signal emitted when a request queued to the camera has completed
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index e5940469..5d2999cb 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
>  	return request->_d()->completeBuffer(buffer);
>  }
>
> +/**
> + * \brief Complete part of metadata for a request
> + * \param[in] request The request the buffer belongs to

    * \param[in] request The Request the metadata buffer belongs to


> + * \param[in] metadata The partial metadata that has completed
> + *
> + * This function could be called by pipeline handlers to signal completion of
> + * the \a metadata part of the \a request. It notifies applications of metadata
> + * completion.

I think a little more details are needed.

    * This function could be called by pipeline handlers to signal
    * availability of \a metadata before \a request completes. Early
    * metadata completion allows to notify applications about the
    * availability of a partial metadata buffer before the associated
    * Request has completed.

1) According to this implementation metadata can be completed
out-of-order (partial metadata for Request X+1 can be signalled before
Request X completes). Is this desirable ?

> + *
> + * \context This function shall be called from the CameraManager thread.
> + */
> +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> +{
> +	const ControlList &validMetadata = request->addCompletedMetadata(metadata);

Request::addCompleteMetadata constructs and returns a ControlList,
I'm surprised the compiler doesn't complain you're taking a reference
to a temporary object (it's apparently legal for const references only).

However, as a temporary will be constructed anyway this doesn't
buy you any gain, possibily the contrary, as this will disallow C++
copy elision from happening (afaiu). I think you should drop &


> +	if (!validMetadata.empty()) {
> +		request->metadata().merge(validMetadata);
> +
> +		Camera *camera = request->_d()->camera();
> +		camera->metadataCompleted.emit(request, validMetadata);
> +	}
> +}
> +
>  /**
>   * \brief Signal request completion
>   * \param[in] request The request that has completed
> @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
>
>  	Camera::Private *data = camera->_d();
>
> +	/*
> +	 * Collect metadata which is not yet completed by the Camera, and
> +	 * create one partial result to cover the missing metadata before
> +	 * completing the whole request. This guarantees the aggregation of
> +	 * metadata in completed partial results equals to the global metadata
> +	 * in the request.
> +	 *
> +	 * \todo: Forbid merging metadata into request.metadata() directly and
> +	 * force calling completeMetadata() to report metadata.
> +	 */
> +	const ControlList &validMetadata = request->addCompletedMetadata(
> +		request->metadata());
> +	if (!validMetadata.empty())
> +		camera->metadataCompleted.emit(request, validMetadata);
> +
>  	while (!data->queuedRequests_.empty()) {
>  		Request *req = data->queuedRequests_.front();
>  		if (req->status() == Request::RequestPending)
> diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> index 8c56ed30..3ce23a8e 100644
> --- a/src/libcamera/request.cpp
> +++ b/src/libcamera/request.cpp
> @@ -598,6 +598,26 @@ std::string Request::toString() const
>  	return ss.str();
>  }
>
> +/**
> + * \brief Add completed metadata, as a partial result
> + * \param[in] metadata The metadata completed
> + *
> + * Request will record the entries that has been sent to the application, to
> + * prevent duplicated controls.
> + *
> + * \return ControlList that hasn't been completed before
> + */
> +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> +{
> +	ControlList resultMetadata;
> +	for (auto &[id, value] : metadata) {
> +		if (!completedMetadata_.count(id))

completedMetadata_ should be cleared in Request::reset().

I wonder if we should actually prevent the same metadata to be
returned multiple times by the pipeline handler or not.

> +			resultMetadata.set(id, value);
> +	}
> +
> +	return resultMetadata;
> +}
> +
>  /**
>   * \brief Insert a text representation of a Request into an output stream
>   * \param[in] out The output stream
> --
> 2.46.0.598.g6f2099f65c-goog
>
Cheng-Hao Yang Nov. 14, 2024, 6:29 a.m. UTC | #3
Hi Jacopo,

I'll upload a new version when all discussion is done.

On Wed, Nov 13, 2024 at 6:47 PM Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Harvey,
>    sorry for the late feedback
>
> On Thu, Sep 12, 2024 at 04:52:48AM +0000, Harvey Yang wrote:
> > From: Han-Lin Chen <hanlinchen@chromium.org>
> >
> > Allows pipeline handler to signal metadata completion by adding the
> > following signals to Camera:
> >
> > Signal<Request *, const ControlList &> metadataCompleted;
>
> Bikeshedding on the name a bit, as this is allowed to be called
> multiple times, with partial metadata, I would call it
> 'metadataAvailable'.

Sure, 'metadataAvailable' does make more sense.
Thanks!

>
> >
> > Together with the bufferCompleted signal, the pipeline handler is allowed to
> > return buffers and partial metadata at any stage of processing.
> >
> > Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> > Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> > ---
> >  include/libcamera/camera.h                    |  1 +
> >  include/libcamera/internal/pipeline_handler.h |  1 +
> >  include/libcamera/request.h                   |  5 +++
> >  src/libcamera/camera.cpp                      |  6 +++
> >  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
> >  src/libcamera/request.cpp                     | 20 ++++++++++
> >  6 files changed, 70 insertions(+)
> >
> > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> > index 94cee7bd..08c5c58c 100644
> > --- a/include/libcamera/camera.h
> > +++ b/include/libcamera/camera.h
> > @@ -122,6 +122,7 @@ public:
> >
> >       const std::string &id() const;
> >
> > +     Signal<Request *, const ControlList &> metadataCompleted;
> >       Signal<Request *, FrameBuffer *> bufferCompleted;
> >       Signal<Request *> requestCompleted;
> >       Signal<> disconnected;
> > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > index 0d380803..1af63a23 100644
> > --- a/include/libcamera/internal/pipeline_handler.h
> > +++ b/include/libcamera/internal/pipeline_handler.h
> > @@ -58,6 +58,7 @@ public:
> >       void registerRequest(Request *request);
> >       void queueRequest(Request *request);
> >
> > +     void completeMetadata(Request *request, const ControlList &metadata);
> >       bool completeBuffer(Request *request, FrameBuffer *buffer);
> >       void completeRequest(Request *request);
> >
> > diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> > index e214a9d1..0200f4a1 100644
> > --- a/include/libcamera/request.h
> > +++ b/include/libcamera/request.h
> > @@ -12,6 +12,7 @@
> >  #include <ostream>
> >  #include <stdint.h>
> >  #include <string>
> > +#include <unordered_set>
> >
> >  #include <libcamera/base/class.h>
> >  #include <libcamera/base/signal.h>
> > @@ -64,6 +65,8 @@ public:
> >
> >       std::string toString() const;
> >
> > +     ControlList addCompletedMetadata(const ControlList &metadata);
> > +
> >  private:
> >       LIBCAMERA_DISABLE_COPY(Request)
> >
> > @@ -73,6 +76,8 @@ private:
> >
> >       const uint64_t cookie_;
> >       Status status_;
> > +
> > +     std::unordered_set<unsigned int> completedMetadata_;
> >  };
> >
> >  std::ostream &operator<<(std::ostream &out, const Request &r);
> > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > index a86f552a..5ffae23e 100644
> > --- a/src/libcamera/camera.cpp
> > +++ b/src/libcamera/camera.cpp
> > @@ -892,6 +892,12 @@ const std::string &Camera::id() const
> >   * completed
> >   */
> >
> > +/**
> > + * \var Camera::metadataCompleted
> > + * \brief Signal emitted when some metadata for a request is completed as a
> > + * partial result
>
> Currently, we return metadata as part of a Request signalled with
> Camera::requestCompleted. Existing applications do not handle the new
> signal and I don't think we should make it mandatory to do so.
> According to the implementation of this patch, the same application
> running on a pipeline handler that calls
> PipelineHandler::completeMetadata and on a pipeline handler that
> doesn't will receive different sets of metadata.
>
> - If a pipeline handler calls PipelineHandler::completeMetadata() it
>   will send metadata in chunks and only the ones not previously
>   signalled with be part of Request::metadata().

Actually, it's not true.
In this patch, `PipelineHandler::completeMetadata` will merge the
partially available metadata into Request:
`request->metadata().merge(validMetadata);`

Therefore, whether an application handles the new signal, it can
still get access to all the metadata in a request via
`Request.metadata()`. This should ease your concern of the old
and new implementations of applications.

>
> - If a pipeline handler doesn't call PipelineHandler::completeMetadata()
>   the full metadata buffer will be in Request::metadata().
>
> How does an application know what to expect ?
>
> Imo we should advertise if the camera supports partial metadata or not
> (maybe through a property). If it does applications should opt-in to
> receive partial metadata. This could even be done per-configuration by
> adding a field to CameraConfiguration ?
>
> If applications enable early metdata completion they agree to handle
> the new signal, and the behaviour will be the one implemented in this
> patch. If they do not enable early metadata completion then
> PipelineHandler::completeMetadata() should simply merge the partial
> metadata in Request::metadata() and not emit the new signal but return
> the full metadata buffer as part of the Request in requestCompleted,
> as they currently do so that pipelines that use early completion and
> pipelines that do not will behave identically from an application
> perspective.
>
> What do you think ?
>
> > + */
> > +
> >  /**
> >   * \var Camera::requestCompleted
> >   * \brief Signal emitted when a request queued to the camera has completed
> > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > index e5940469..5d2999cb 100644
> > --- a/src/libcamera/pipeline_handler.cpp
> > +++ b/src/libcamera/pipeline_handler.cpp
> > @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
> >       return request->_d()->completeBuffer(buffer);
> >  }
> >
> > +/**
> > + * \brief Complete part of metadata for a request
> > + * \param[in] request The request the buffer belongs to
>
>     * \param[in] request The Request the metadata buffer belongs to

Perhaps:
`* \param[in] request The Request the metadata belongs to`
?

>
>
> > + * \param[in] metadata The partial metadata that has completed
> > + *
> > + * This function could be called by pipeline handlers to signal completion of
> > + * the \a metadata part of the \a request. It notifies applications of metadata
> > + * completion.
>
> I think a little more details are needed.
>
>     * This function could be called by pipeline handlers to signal
>     * availability of \a metadata before \a request completes. Early
>     * metadata completion allows to notify applications about the
>     * availability of a partial metadata buffer before the associated
>     * Request has completed.

Thanks! Updated.

>
> 1) According to this implementation metadata can be completed
> out-of-order (partial metadata for Request X+1 can be signalled before
> Request X completes). Is this desirable ?

Yes, this is what we expect, and Android also allows that.
WDYT?

>
> > + *
> > + * \context This function shall be called from the CameraManager thread.
> > + */
> > +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> > +{
> > +     const ControlList &validMetadata = request->addCompletedMetadata(metadata);
>
> Request::addCompleteMetadata constructs and returns a ControlList,
> I'm surprised the compiler doesn't complain you're taking a reference
> to a temporary object (it's apparently legal for const references only).
>
> However, as a temporary will be constructed anyway this doesn't
> buy you any gain, possibily the contrary, as this will disallow C++
> copy elision from happening (afaiu). I think you should drop &

Right, it's definitely a mistake. Removed.

>
>
> > +     if (!validMetadata.empty()) {
> > +             request->metadata().merge(validMetadata);
> > +
> > +             Camera *camera = request->_d()->camera();
> > +             camera->metadataCompleted.emit(request, validMetadata);
> > +     }
> > +}
> > +
> >  /**
> >   * \brief Signal request completion
> >   * \param[in] request The request that has completed
> > @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
> >
> >       Camera::Private *data = camera->_d();
> >
> > +     /*
> > +      * Collect metadata which is not yet completed by the Camera, and
> > +      * create one partial result to cover the missing metadata before
> > +      * completing the whole request. This guarantees the aggregation of
> > +      * metadata in completed partial results equals to the global metadata
> > +      * in the request.
> > +      *
> > +      * \todo: Forbid merging metadata into request.metadata() directly and
> > +      * force calling completeMetadata() to report metadata.
> > +      */
> > +     const ControlList &validMetadata = request->addCompletedMetadata(
> > +             request->metadata());
> > +     if (!validMetadata.empty())
> > +             camera->metadataCompleted.emit(request, validMetadata);
> > +
> >       while (!data->queuedRequests_.empty()) {
> >               Request *req = data->queuedRequests_.front();
> >               if (req->status() == Request::RequestPending)
> > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> > index 8c56ed30..3ce23a8e 100644
> > --- a/src/libcamera/request.cpp
> > +++ b/src/libcamera/request.cpp
> > @@ -598,6 +598,26 @@ std::string Request::toString() const
> >       return ss.str();
> >  }
> >
> > +/**
> > + * \brief Add completed metadata, as a partial result
> > + * \param[in] metadata The metadata completed
> > + *
> > + * Request will record the entries that has been sent to the application, to
> > + * prevent duplicated controls.
> > + *
> > + * \return ControlList that hasn't been completed before
> > + */
> > +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> > +{
> > +     ControlList resultMetadata;
> > +     for (auto &[id, value] : metadata) {
> > +             if (!completedMetadata_.count(id))
>
> completedMetadata_ should be cleared in Request::reset().

Nice catch! Then how about we move the member variable and the member
function to `Request::Private` instead?

>
> I wonder if we should actually prevent the same metadata to be
> returned multiple times by the pipeline handler or not.

This is actually prohibited by Android camera hal [1]:
`Partial metadata submitted should not include any metadata key
returned in a previous partial result for a given frame.`

We can certainly prevent that in Android Adapter only, while I think
having this restriction in libcamera core library also makes sense.
WDYT? I can also update the description of the new signal.

[1]: https://source.android.com/reference/hal/structcamera3__capture__result#:~:text=partial_result-,Detailed%20Description,the%20result%20metadata%20to%20NULL.

BR,
Harvey
>
> > +                     resultMetadata.set(id, value);
> > +     }
> > +
> > +     return resultMetadata;
> > +}
> > +
> >  /**
> >   * \brief Insert a text representation of a Request into an output stream
> >   * \param[in] out The output stream
> > --
> > 2.46.0.598.g6f2099f65c-goog
> >
Jacopo Mondi Nov. 14, 2024, 8:36 a.m. UTC | #4
Hi Harvey

On Thu, Nov 14, 2024 at 02:29:20PM +0800, Cheng-Hao Yang wrote:
> Hi Jacopo,
>
> I'll upload a new version when all discussion is done.
>
> On Wed, Nov 13, 2024 at 6:47 PM Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hi Harvey,
> >    sorry for the late feedback
> >
> > On Thu, Sep 12, 2024 at 04:52:48AM +0000, Harvey Yang wrote:
> > > From: Han-Lin Chen <hanlinchen@chromium.org>
> > >
> > > Allows pipeline handler to signal metadata completion by adding the
> > > following signals to Camera:
> > >
> > > Signal<Request *, const ControlList &> metadataCompleted;
> >
> > Bikeshedding on the name a bit, as this is allowed to be called
> > multiple times, with partial metadata, I would call it
> > 'metadataAvailable'.
>
> Sure, 'metadataAvailable' does make more sense.
> Thanks!
>
> >
> > >
> > > Together with the bufferCompleted signal, the pipeline handler is allowed to
> > > return buffers and partial metadata at any stage of processing.
> > >
> > > Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> > > Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> > > ---
> > >  include/libcamera/camera.h                    |  1 +
> > >  include/libcamera/internal/pipeline_handler.h |  1 +
> > >  include/libcamera/request.h                   |  5 +++
> > >  src/libcamera/camera.cpp                      |  6 +++
> > >  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
> > >  src/libcamera/request.cpp                     | 20 ++++++++++
> > >  6 files changed, 70 insertions(+)
> > >
> > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> > > index 94cee7bd..08c5c58c 100644
> > > --- a/include/libcamera/camera.h
> > > +++ b/include/libcamera/camera.h
> > > @@ -122,6 +122,7 @@ public:
> > >
> > >       const std::string &id() const;
> > >
> > > +     Signal<Request *, const ControlList &> metadataCompleted;
> > >       Signal<Request *, FrameBuffer *> bufferCompleted;
> > >       Signal<Request *> requestCompleted;
> > >       Signal<> disconnected;
> > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > > index 0d380803..1af63a23 100644
> > > --- a/include/libcamera/internal/pipeline_handler.h
> > > +++ b/include/libcamera/internal/pipeline_handler.h
> > > @@ -58,6 +58,7 @@ public:
> > >       void registerRequest(Request *request);
> > >       void queueRequest(Request *request);
> > >
> > > +     void completeMetadata(Request *request, const ControlList &metadata);
> > >       bool completeBuffer(Request *request, FrameBuffer *buffer);
> > >       void completeRequest(Request *request);
> > >
> > > diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> > > index e214a9d1..0200f4a1 100644
> > > --- a/include/libcamera/request.h
> > > +++ b/include/libcamera/request.h
> > > @@ -12,6 +12,7 @@
> > >  #include <ostream>
> > >  #include <stdint.h>
> > >  #include <string>
> > > +#include <unordered_set>
> > >
> > >  #include <libcamera/base/class.h>
> > >  #include <libcamera/base/signal.h>
> > > @@ -64,6 +65,8 @@ public:
> > >
> > >       std::string toString() const;
> > >
> > > +     ControlList addCompletedMetadata(const ControlList &metadata);
> > > +
> > >  private:
> > >       LIBCAMERA_DISABLE_COPY(Request)
> > >
> > > @@ -73,6 +76,8 @@ private:
> > >
> > >       const uint64_t cookie_;
> > >       Status status_;
> > > +
> > > +     std::unordered_set<unsigned int> completedMetadata_;
> > >  };
> > >
> > >  std::ostream &operator<<(std::ostream &out, const Request &r);
> > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > > index a86f552a..5ffae23e 100644
> > > --- a/src/libcamera/camera.cpp
> > > +++ b/src/libcamera/camera.cpp
> > > @@ -892,6 +892,12 @@ const std::string &Camera::id() const
> > >   * completed
> > >   */
> > >
> > > +/**
> > > + * \var Camera::metadataCompleted
> > > + * \brief Signal emitted when some metadata for a request is completed as a
> > > + * partial result
> >
> > Currently, we return metadata as part of a Request signalled with
> > Camera::requestCompleted. Existing applications do not handle the new
> > signal and I don't think we should make it mandatory to do so.
> > According to the implementation of this patch, the same application
> > running on a pipeline handler that calls
> > PipelineHandler::completeMetadata and on a pipeline handler that
> > doesn't will receive different sets of metadata.
> >
> > - If a pipeline handler calls PipelineHandler::completeMetadata() it
> >   will send metadata in chunks and only the ones not previously
> >   signalled with be part of Request::metadata().
>
> Actually, it's not true.
> In this patch, `PipelineHandler::completeMetadata` will merge the
> partially available metadata into Request:
> `request->metadata().merge(validMetadata);`
>

Ups, sorry I missed it!

> Therefore, whether an application handles the new signal, it can
> still get access to all the metadata in a request via
> `Request.metadata()`. This should ease your concern of the old
> and new implementations of applications.
>

Yes, if this time I got this right, the full metadata pack will be
available in Request::metadata() at requestCompleted time, but it will
be completed in chunks if the pipeline handler calls
completeMetadata() earlier.

There's only one thing left that I'm not sure about: in
requestCompleted()

	const ControlList &validMetadata = request->addCompletedMetadata(
		request->metadata());
	if (!validMetadata.empty())
		camera->metadataCompleted.emit(request, validMetadata);

If the pipeline handler never calls completeMetadata() then
validMetadata == request->metadata(). So that applications will
receive the same metadata buffer in metadataCompleted and
requestCompleted. I wonder if we should emit the metadataCompleted
signal at all if the pipeline handler never calls completeMetadata()
(it would be easy to check, just make sure that Request:completedMetadata_
is empty with an helper function).

> >
> > - If a pipeline handler doesn't call PipelineHandler::completeMetadata()
> >   the full metadata buffer will be in Request::metadata().
> >
> > How does an application know what to expect ?
> >
> > Imo we should advertise if the camera supports partial metadata or not
> > (maybe through a property). If it does applications should opt-in to
> > receive partial metadata. This could even be done per-configuration by
> > adding a field to CameraConfiguration ?
> >
> > If applications enable early metdata completion they agree to handle
> > the new signal, and the behaviour will be the one implemented in this
> > patch. If they do not enable early metadata completion then
> > PipelineHandler::completeMetadata() should simply merge the partial
> > metadata in Request::metadata() and not emit the new signal but return
> > the full metadata buffer as part of the Request in requestCompleted,
> > as they currently do so that pipelines that use early completion and
> > pipelines that do not will behave identically from an application
> > perspective.
> >
> > What do you think ?
> >
> > > + */
> > > +
> > >  /**
> > >   * \var Camera::requestCompleted
> > >   * \brief Signal emitted when a request queued to the camera has completed
> > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > index e5940469..5d2999cb 100644
> > > --- a/src/libcamera/pipeline_handler.cpp
> > > +++ b/src/libcamera/pipeline_handler.cpp
> > > @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
> > >       return request->_d()->completeBuffer(buffer);
> > >  }
> > >
> > > +/**
> > > + * \brief Complete part of metadata for a request
> > > + * \param[in] request The request the buffer belongs to
> >
> >     * \param[in] request The Request the metadata buffer belongs to
>
> Perhaps:
> `* \param[in] request The Request the metadata belongs to`
> ?
>

ack

> >
> >
> > > + * \param[in] metadata The partial metadata that has completed
> > > + *
> > > + * This function could be called by pipeline handlers to signal completion of
> > > + * the \a metadata part of the \a request. It notifies applications of metadata
> > > + * completion.
> >
> > I think a little more details are needed.
> >
> >     * This function could be called by pipeline handlers to signal
> >     * availability of \a metadata before \a request completes. Early
> >     * metadata completion allows to notify applications about the
> >     * availability of a partial metadata buffer before the associated
> >     * Request has completed.
>
> Thanks! Updated.
>
> >
> > 1) According to this implementation metadata can be completed
> > out-of-order (partial metadata for Request X+1 can be signalled before
> > Request X completes). Is this desirable ?
>
> Yes, this is what we expect, and Android also allows that.
> WDYT?
>

I'll ask others what to they think

> >
> > > + *
> > > + * \context This function shall be called from the CameraManager thread.
> > > + */
> > > +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> > > +{
> > > +     const ControlList &validMetadata = request->addCompletedMetadata(metadata);
> >
> > Request::addCompleteMetadata constructs and returns a ControlList,
> > I'm surprised the compiler doesn't complain you're taking a reference
> > to a temporary object (it's apparently legal for const references only).
> >
> > However, as a temporary will be constructed anyway this doesn't
> > buy you any gain, possibily the contrary, as this will disallow C++
> > copy elision from happening (afaiu). I think you should drop &
>
> Right, it's definitely a mistake. Removed.
>
> >
> >
> > > +     if (!validMetadata.empty()) {
> > > +             request->metadata().merge(validMetadata);
> > > +
> > > +             Camera *camera = request->_d()->camera();
> > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > +     }
> > > +}
> > > +
> > >  /**
> > >   * \brief Signal request completion
> > >   * \param[in] request The request that has completed
> > > @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
> > >
> > >       Camera::Private *data = camera->_d();
> > >
> > > +     /*
> > > +      * Collect metadata which is not yet completed by the Camera, and
> > > +      * create one partial result to cover the missing metadata before
> > > +      * completing the whole request. This guarantees the aggregation of
> > > +      * metadata in completed partial results equals to the global metadata
> > > +      * in the request.
> > > +      *
> > > +      * \todo: Forbid merging metadata into request.metadata() directly and
> > > +      * force calling completeMetadata() to report metadata.
> > > +      */

Could you please clarify me what this \todo means ?

> > > +     const ControlList &validMetadata = request->addCompletedMetadata(
> > > +             request->metadata());
> > > +     if (!validMetadata.empty())
> > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > +
> > >       while (!data->queuedRequests_.empty()) {
> > >               Request *req = data->queuedRequests_.front();
> > >               if (req->status() == Request::RequestPending)
> > > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> > > index 8c56ed30..3ce23a8e 100644
> > > --- a/src/libcamera/request.cpp
> > > +++ b/src/libcamera/request.cpp
> > > @@ -598,6 +598,26 @@ std::string Request::toString() const
> > >       return ss.str();
> > >  }
> > >
> > > +/**
> > > + * \brief Add completed metadata, as a partial result
> > > + * \param[in] metadata The metadata completed
> > > + *
> > > + * Request will record the entries that has been sent to the application, to
> > > + * prevent duplicated controls.
> > > + *
> > > + * \return ControlList that hasn't been completed before
> > > + */
> > > +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> > > +{
> > > +     ControlList resultMetadata;
> > > +     for (auto &[id, value] : metadata) {
> > > +             if (!completedMetadata_.count(id))
> >
> > completedMetadata_ should be cleared in Request::reset().
>
> Nice catch! Then how about we move the member variable and the member
> function to `Request::Private` instead?
>

Yes, they both do not need to be exposed to applications indeed.

> >
> > I wonder if we should actually prevent the same metadata to be
> > returned multiple times by the pipeline handler or not.
>
> This is actually prohibited by Android camera hal [1]:
> `Partial metadata submitted should not include any metadata key
> returned in a previous partial result for a given frame.`
>
> We can certainly prevent that in Android Adapter only, while I think
> having this restriction in libcamera core library also makes sense.
> WDYT? I can also update the description of the new signal.
>
> [1]: https://source.android.com/reference/hal/structcamera3__capture__result#:~:text=partial_result-,Detailed%20Description,the%20result%20metadata%20to%20NULL.

I can't find in the text where this is forbidden, however I'm not
pushing for the other option, I think it's actually saner to only
signal a metadata once per Request. I wonder however if this shouldn't
be a WARN as it means the pipeline handler is doing something
unexpected. Also, the expectations that a metadata is returned once
per Request has to be documented in the documentation of
PipelineHandler::completeMetadata()

Thanks
  j

>
> BR,
> Harvey
> >
> > > +                     resultMetadata.set(id, value);
> > > +     }
> > > +
> > > +     return resultMetadata;
> > > +}
> > > +
> > >  /**
> > >   * \brief Insert a text representation of a Request into an output stream
> > >   * \param[in] out The output stream
> > > --
> > > 2.46.0.598.g6f2099f65c-goog
> > >
Cheng-Hao Yang Nov. 14, 2024, 9:11 a.m. UTC | #5
Hi Jacopo,

On Thu, Nov 14, 2024 at 4:36 PM Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Harvey
>
> On Thu, Nov 14, 2024 at 02:29:20PM +0800, Cheng-Hao Yang wrote:
> > Hi Jacopo,
> >
> > I'll upload a new version when all discussion is done.
> >
> > On Wed, Nov 13, 2024 at 6:47 PM Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hi Harvey,
> > >    sorry for the late feedback
> > >
> > > On Thu, Sep 12, 2024 at 04:52:48AM +0000, Harvey Yang wrote:
> > > > From: Han-Lin Chen <hanlinchen@chromium.org>
> > > >
> > > > Allows pipeline handler to signal metadata completion by adding the
> > > > following signals to Camera:
> > > >
> > > > Signal<Request *, const ControlList &> metadataCompleted;
> > >
> > > Bikeshedding on the name a bit, as this is allowed to be called
> > > multiple times, with partial metadata, I would call it
> > > 'metadataAvailable'.
> >
> > Sure, 'metadataAvailable' does make more sense.
> > Thanks!
> >
> > >
> > > >
> > > > Together with the bufferCompleted signal, the pipeline handler is allowed to
> > > > return buffers and partial metadata at any stage of processing.
> > > >
> > > > Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> > > > Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> > > > ---
> > > >  include/libcamera/camera.h                    |  1 +
> > > >  include/libcamera/internal/pipeline_handler.h |  1 +
> > > >  include/libcamera/request.h                   |  5 +++
> > > >  src/libcamera/camera.cpp                      |  6 +++
> > > >  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
> > > >  src/libcamera/request.cpp                     | 20 ++++++++++
> > > >  6 files changed, 70 insertions(+)
> > > >
> > > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> > > > index 94cee7bd..08c5c58c 100644
> > > > --- a/include/libcamera/camera.h
> > > > +++ b/include/libcamera/camera.h
> > > > @@ -122,6 +122,7 @@ public:
> > > >
> > > >       const std::string &id() const;
> > > >
> > > > +     Signal<Request *, const ControlList &> metadataCompleted;
> > > >       Signal<Request *, FrameBuffer *> bufferCompleted;
> > > >       Signal<Request *> requestCompleted;
> > > >       Signal<> disconnected;
> > > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > > > index 0d380803..1af63a23 100644
> > > > --- a/include/libcamera/internal/pipeline_handler.h
> > > > +++ b/include/libcamera/internal/pipeline_handler.h
> > > > @@ -58,6 +58,7 @@ public:
> > > >       void registerRequest(Request *request);
> > > >       void queueRequest(Request *request);
> > > >
> > > > +     void completeMetadata(Request *request, const ControlList &metadata);
> > > >       bool completeBuffer(Request *request, FrameBuffer *buffer);
> > > >       void completeRequest(Request *request);
> > > >
> > > > diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> > > > index e214a9d1..0200f4a1 100644
> > > > --- a/include/libcamera/request.h
> > > > +++ b/include/libcamera/request.h
> > > > @@ -12,6 +12,7 @@
> > > >  #include <ostream>
> > > >  #include <stdint.h>
> > > >  #include <string>
> > > > +#include <unordered_set>
> > > >
> > > >  #include <libcamera/base/class.h>
> > > >  #include <libcamera/base/signal.h>
> > > > @@ -64,6 +65,8 @@ public:
> > > >
> > > >       std::string toString() const;
> > > >
> > > > +     ControlList addCompletedMetadata(const ControlList &metadata);
> > > > +
> > > >  private:
> > > >       LIBCAMERA_DISABLE_COPY(Request)
> > > >
> > > > @@ -73,6 +76,8 @@ private:
> > > >
> > > >       const uint64_t cookie_;
> > > >       Status status_;
> > > > +
> > > > +     std::unordered_set<unsigned int> completedMetadata_;
> > > >  };
> > > >
> > > >  std::ostream &operator<<(std::ostream &out, const Request &r);
> > > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > > > index a86f552a..5ffae23e 100644
> > > > --- a/src/libcamera/camera.cpp
> > > > +++ b/src/libcamera/camera.cpp
> > > > @@ -892,6 +892,12 @@ const std::string &Camera::id() const
> > > >   * completed
> > > >   */
> > > >
> > > > +/**
> > > > + * \var Camera::metadataCompleted
> > > > + * \brief Signal emitted when some metadata for a request is completed as a
> > > > + * partial result
> > >
> > > Currently, we return metadata as part of a Request signalled with
> > > Camera::requestCompleted. Existing applications do not handle the new
> > > signal and I don't think we should make it mandatory to do so.
> > > According to the implementation of this patch, the same application
> > > running on a pipeline handler that calls
> > > PipelineHandler::completeMetadata and on a pipeline handler that
> > > doesn't will receive different sets of metadata.
> > >
> > > - If a pipeline handler calls PipelineHandler::completeMetadata() it
> > >   will send metadata in chunks and only the ones not previously
> > >   signalled with be part of Request::metadata().
> >
> > Actually, it's not true.
> > In this patch, `PipelineHandler::completeMetadata` will merge the
> > partially available metadata into Request:
> > `request->metadata().merge(validMetadata);`
> >
>
> Ups, sorry I missed it!
>
> > Therefore, whether an application handles the new signal, it can
> > still get access to all the metadata in a request via
> > `Request.metadata()`. This should ease your concern of the old
> > and new implementations of applications.
> >
>
> Yes, if this time I got this right, the full metadata pack will be
> available in Request::metadata() at requestCompleted time, but it will
> be completed in chunks if the pipeline handler calls
> completeMetadata() earlier.
>
> There's only one thing left that I'm not sure about: in
> requestCompleted()
>
>         const ControlList &validMetadata = request->addCompletedMetadata(
>                 request->metadata());
>         if (!validMetadata.empty())
>                 camera->metadataCompleted.emit(request, validMetadata);
>
> If the pipeline handler never calls completeMetadata() then
> validMetadata == request->metadata(). So that applications will
> receive the same metadata buffer in metadataCompleted and
> requestCompleted. I wonder if we should emit the metadataCompleted
> signal at all if the pipeline handler never calls completeMetadata()
> (it would be easy to check, just make sure that Request:completedMetadata_
> is empty with an helper function).

I think this is how we expect a pipeline handler to behave and how an
application to handle signals.

In the current implementation, it's safer for applications that whether
a pipeline handler calls completeMetadata, applications still get full
metadata set with the new signal, and they don't need to rely on
signal requestCompleted. In other words, I want to guarantee that
applications can choose to use either metadataAvailable or
requestCompleted.
Triggering the new signal if there's any metadata left is just a safer
option to support all pipeline handlers.

Of course we can choose to check if
`Request::Private::completedMetadata_` is empty, and only handle
this case, and expect the proper implementations of pipeline
handlers call completeMetadata properly without missing any
metadata. I just feel that it's unnecessary.
Also, I'm not sure if there's a case that pipeline handler doesn't
want to report some metadata earlier, and would rather send
them altogether when a request is completed. In this case,
your approach would require a pipeline handler to record
those unsent metadata on its own.

WDYT?

>
> > >
> > > - If a pipeline handler doesn't call PipelineHandler::completeMetadata()
> > >   the full metadata buffer will be in Request::metadata().
> > >
> > > How does an application know what to expect ?
> > >
> > > Imo we should advertise if the camera supports partial metadata or not
> > > (maybe through a property). If it does applications should opt-in to
> > > receive partial metadata. This could even be done per-configuration by
> > > adding a field to CameraConfiguration ?
> > >
> > > If applications enable early metdata completion they agree to handle
> > > the new signal, and the behaviour will be the one implemented in this
> > > patch. If they do not enable early metadata completion then
> > > PipelineHandler::completeMetadata() should simply merge the partial
> > > metadata in Request::metadata() and not emit the new signal but return
> > > the full metadata buffer as part of the Request in requestCompleted,
> > > as they currently do so that pipelines that use early completion and
> > > pipelines that do not will behave identically from an application
> > > perspective.
> > >
> > > What do you think ?
> > >
> > > > + */
> > > > +
> > > >  /**
> > > >   * \var Camera::requestCompleted
> > > >   * \brief Signal emitted when a request queued to the camera has completed
> > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > > index e5940469..5d2999cb 100644
> > > > --- a/src/libcamera/pipeline_handler.cpp
> > > > +++ b/src/libcamera/pipeline_handler.cpp
> > > > @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
> > > >       return request->_d()->completeBuffer(buffer);
> > > >  }
> > > >
> > > > +/**
> > > > + * \brief Complete part of metadata for a request
> > > > + * \param[in] request The request the buffer belongs to
> > >
> > >     * \param[in] request The Request the metadata buffer belongs to
> >
> > Perhaps:
> > `* \param[in] request The Request the metadata belongs to`
> > ?
> >
>
> ack
>
> > >
> > >
> > > > + * \param[in] metadata The partial metadata that has completed
> > > > + *
> > > > + * This function could be called by pipeline handlers to signal completion of
> > > > + * the \a metadata part of the \a request. It notifies applications of metadata
> > > > + * completion.
> > >
> > > I think a little more details are needed.
> > >
> > >     * This function could be called by pipeline handlers to signal
> > >     * availability of \a metadata before \a request completes. Early
> > >     * metadata completion allows to notify applications about the
> > >     * availability of a partial metadata buffer before the associated
> > >     * Request has completed.
> >
> > Thanks! Updated.
> >
> > >
> > > 1) According to this implementation metadata can be completed
> > > out-of-order (partial metadata for Request X+1 can be signalled before
> > > Request X completes). Is this desirable ?
> >
> > Yes, this is what we expect, and Android also allows that.
> > WDYT?
> >
>
> I'll ask others what to they think
>
> > >
> > > > + *
> > > > + * \context This function shall be called from the CameraManager thread.
> > > > + */
> > > > +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> > > > +{
> > > > +     const ControlList &validMetadata = request->addCompletedMetadata(metadata);
> > >
> > > Request::addCompleteMetadata constructs and returns a ControlList,
> > > I'm surprised the compiler doesn't complain you're taking a reference
> > > to a temporary object (it's apparently legal for const references only).
> > >
> > > However, as a temporary will be constructed anyway this doesn't
> > > buy you any gain, possibily the contrary, as this will disallow C++
> > > copy elision from happening (afaiu). I think you should drop &
> >
> > Right, it's definitely a mistake. Removed.
> >
> > >
> > >
> > > > +     if (!validMetadata.empty()) {
> > > > +             request->metadata().merge(validMetadata);
> > > > +
> > > > +             Camera *camera = request->_d()->camera();
> > > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > > +     }
> > > > +}
> > > > +
> > > >  /**
> > > >   * \brief Signal request completion
> > > >   * \param[in] request The request that has completed
> > > > @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
> > > >
> > > >       Camera::Private *data = camera->_d();
> > > >
> > > > +     /*
> > > > +      * Collect metadata which is not yet completed by the Camera, and
> > > > +      * create one partial result to cover the missing metadata before
> > > > +      * completing the whole request. This guarantees the aggregation of
> > > > +      * metadata in completed partial results equals to the global metadata
> > > > +      * in the request.
> > > > +      *
> > > > +      * \todo: Forbid merging metadata into request.metadata() directly and
> > > > +      * force calling completeMetadata() to report metadata.
> > > > +      */
>
> Could you please clarify me what this \todo means ?

Similar to the above reasons:
Currently all pipeline handlers merge metadata into request.metadata()
directly, except for the upcoming mtkisp7. We were expecting every
pipeline handler to migrate to `PipelineHandler::completeMetadata`
eventually.

Let me know if you think this todo doesn't make sense, as we should
still allow the previous way to merge into request.metadata() directly,
or there's a case that a pipeline handler doesn't want to report some
metadata earlier.

>
> > > > +     const ControlList &validMetadata = request->addCompletedMetadata(
> > > > +             request->metadata());
> > > > +     if (!validMetadata.empty())
> > > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > > +
> > > >       while (!data->queuedRequests_.empty()) {
> > > >               Request *req = data->queuedRequests_.front();
> > > >               if (req->status() == Request::RequestPending)
> > > > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> > > > index 8c56ed30..3ce23a8e 100644
> > > > --- a/src/libcamera/request.cpp
> > > > +++ b/src/libcamera/request.cpp
> > > > @@ -598,6 +598,26 @@ std::string Request::toString() const
> > > >       return ss.str();
> > > >  }
> > > >
> > > > +/**
> > > > + * \brief Add completed metadata, as a partial result
> > > > + * \param[in] metadata The metadata completed
> > > > + *
> > > > + * Request will record the entries that has been sent to the application, to
> > > > + * prevent duplicated controls.
> > > > + *
> > > > + * \return ControlList that hasn't been completed before
> > > > + */
> > > > +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> > > > +{
> > > > +     ControlList resultMetadata;
> > > > +     for (auto &[id, value] : metadata) {
> > > > +             if (!completedMetadata_.count(id))
> > >
> > > completedMetadata_ should be cleared in Request::reset().
> >
> > Nice catch! Then how about we move the member variable and the member
> > function to `Request::Private` instead?
> >
>
> Yes, they both do not need to be exposed to applications indeed.
>
> > >
> > > I wonder if we should actually prevent the same metadata to be
> > > returned multiple times by the pipeline handler or not.
> >
> > This is actually prohibited by Android camera hal [1]:
> > `Partial metadata submitted should not include any metadata key
> > returned in a previous partial result for a given frame.`
> >
> > We can certainly prevent that in Android Adapter only, while I think
> > having this restriction in libcamera core library also makes sense.
> > WDYT? I can also update the description of the new signal.
> >
> > [1]: https://source.android.com/reference/hal/structcamera3__capture__result#:~:text=partial_result-,Detailed%20Description,the%20result%20metadata%20to%20NULL.
>
> I can't find in the text where this is forbidden, however I'm not
> pushing for the other option, I think it's actually saner to only
> signal a metadata once per Request. I wonder however if this shouldn't
> be a WARN as it means the pipeline handler is doing something
> unexpected.

True that a WARN is an option.
I would prefer the current approach though, otherwise I need to
implement the logic in Android Adapter instead.

Please let me know if anyone insists on the other approach.

> Also, the expectations that a metadata is returned once
> per Request has to be documented in the documentation of
> PipelineHandler::completeMetadata()

I'll update the doc in the next version :)

BR,
Harvey

>
> Thanks
>   j
>
> >
> > BR,
> > Harvey
> > >
> > > > +                     resultMetadata.set(id, value);
> > > > +     }
> > > > +
> > > > +     return resultMetadata;
> > > > +}
> > > > +
> > > >  /**
> > > >   * \brief Insert a text representation of a Request into an output stream
> > > >   * \param[in] out The output stream
> > > > --
> > > > 2.46.0.598.g6f2099f65c-goog
> > > >
Jacopo Mondi Nov. 14, 2024, 10:17 a.m. UTC | #6
Hi Harvey

On Thu, Nov 14, 2024 at 05:11:42PM +0800, Cheng-Hao Yang wrote:
> Hi Jacopo,
>
> On Thu, Nov 14, 2024 at 4:36 PM Jacopo Mondi
> <jacopo.mondi@ideasonboard.com> wrote:
> >
> > Hi Harvey
> >
> > On Thu, Nov 14, 2024 at 02:29:20PM +0800, Cheng-Hao Yang wrote:
> > > Hi Jacopo,
> > >
> > > I'll upload a new version when all discussion is done.
> > >
> > > On Wed, Nov 13, 2024 at 6:47 PM Jacopo Mondi
> > > <jacopo.mondi@ideasonboard.com> wrote:
> > > >
> > > > Hi Harvey,
> > > >    sorry for the late feedback
> > > >
> > > > On Thu, Sep 12, 2024 at 04:52:48AM +0000, Harvey Yang wrote:
> > > > > From: Han-Lin Chen <hanlinchen@chromium.org>
> > > > >
> > > > > Allows pipeline handler to signal metadata completion by adding the
> > > > > following signals to Camera:
> > > > >
> > > > > Signal<Request *, const ControlList &> metadataCompleted;
> > > >
> > > > Bikeshedding on the name a bit, as this is allowed to be called
> > > > multiple times, with partial metadata, I would call it
> > > > 'metadataAvailable'.
> > >
> > > Sure, 'metadataAvailable' does make more sense.
> > > Thanks!
> > >
> > > >
> > > > >
> > > > > Together with the bufferCompleted signal, the pipeline handler is allowed to
> > > > > return buffers and partial metadata at any stage of processing.
> > > > >
> > > > > Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> > > > > Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> > > > > ---
> > > > >  include/libcamera/camera.h                    |  1 +
> > > > >  include/libcamera/internal/pipeline_handler.h |  1 +
> > > > >  include/libcamera/request.h                   |  5 +++
> > > > >  src/libcamera/camera.cpp                      |  6 +++
> > > > >  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
> > > > >  src/libcamera/request.cpp                     | 20 ++++++++++
> > > > >  6 files changed, 70 insertions(+)
> > > > >
> > > > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> > > > > index 94cee7bd..08c5c58c 100644
> > > > > --- a/include/libcamera/camera.h
> > > > > +++ b/include/libcamera/camera.h
> > > > > @@ -122,6 +122,7 @@ public:
> > > > >
> > > > >       const std::string &id() const;
> > > > >
> > > > > +     Signal<Request *, const ControlList &> metadataCompleted;
> > > > >       Signal<Request *, FrameBuffer *> bufferCompleted;
> > > > >       Signal<Request *> requestCompleted;
> > > > >       Signal<> disconnected;
> > > > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > > > > index 0d380803..1af63a23 100644
> > > > > --- a/include/libcamera/internal/pipeline_handler.h
> > > > > +++ b/include/libcamera/internal/pipeline_handler.h
> > > > > @@ -58,6 +58,7 @@ public:
> > > > >       void registerRequest(Request *request);
> > > > >       void queueRequest(Request *request);
> > > > >
> > > > > +     void completeMetadata(Request *request, const ControlList &metadata);
> > > > >       bool completeBuffer(Request *request, FrameBuffer *buffer);
> > > > >       void completeRequest(Request *request);
> > > > >
> > > > > diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> > > > > index e214a9d1..0200f4a1 100644
> > > > > --- a/include/libcamera/request.h
> > > > > +++ b/include/libcamera/request.h
> > > > > @@ -12,6 +12,7 @@
> > > > >  #include <ostream>
> > > > >  #include <stdint.h>
> > > > >  #include <string>
> > > > > +#include <unordered_set>
> > > > >
> > > > >  #include <libcamera/base/class.h>
> > > > >  #include <libcamera/base/signal.h>
> > > > > @@ -64,6 +65,8 @@ public:
> > > > >
> > > > >       std::string toString() const;
> > > > >
> > > > > +     ControlList addCompletedMetadata(const ControlList &metadata);
> > > > > +
> > > > >  private:
> > > > >       LIBCAMERA_DISABLE_COPY(Request)
> > > > >
> > > > > @@ -73,6 +76,8 @@ private:
> > > > >
> > > > >       const uint64_t cookie_;
> > > > >       Status status_;
> > > > > +
> > > > > +     std::unordered_set<unsigned int> completedMetadata_;
> > > > >  };
> > > > >
> > > > >  std::ostream &operator<<(std::ostream &out, const Request &r);
> > > > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > > > > index a86f552a..5ffae23e 100644
> > > > > --- a/src/libcamera/camera.cpp
> > > > > +++ b/src/libcamera/camera.cpp
> > > > > @@ -892,6 +892,12 @@ const std::string &Camera::id() const
> > > > >   * completed
> > > > >   */
> > > > >
> > > > > +/**
> > > > > + * \var Camera::metadataCompleted
> > > > > + * \brief Signal emitted when some metadata for a request is completed as a
> > > > > + * partial result
> > > >
> > > > Currently, we return metadata as part of a Request signalled with
> > > > Camera::requestCompleted. Existing applications do not handle the new
> > > > signal and I don't think we should make it mandatory to do so.
> > > > According to the implementation of this patch, the same application
> > > > running on a pipeline handler that calls
> > > > PipelineHandler::completeMetadata and on a pipeline handler that
> > > > doesn't will receive different sets of metadata.
> > > >
> > > > - If a pipeline handler calls PipelineHandler::completeMetadata() it
> > > >   will send metadata in chunks and only the ones not previously
> > > >   signalled with be part of Request::metadata().
> > >
> > > Actually, it's not true.
> > > In this patch, `PipelineHandler::completeMetadata` will merge the
> > > partially available metadata into Request:
> > > `request->metadata().merge(validMetadata);`
> > >
> >
> > Ups, sorry I missed it!
> >
> > > Therefore, whether an application handles the new signal, it can
> > > still get access to all the metadata in a request via
> > > `Request.metadata()`. This should ease your concern of the old
> > > and new implementations of applications.
> > >
> >
> > Yes, if this time I got this right, the full metadata pack will be
> > available in Request::metadata() at requestCompleted time, but it will
> > be completed in chunks if the pipeline handler calls
> > completeMetadata() earlier.
> >
> > There's only one thing left that I'm not sure about: in
> > requestCompleted()
> >
> >         const ControlList &validMetadata = request->addCompletedMetadata(
> >                 request->metadata());
> >         if (!validMetadata.empty())
> >                 camera->metadataCompleted.emit(request, validMetadata);
> >
> > If the pipeline handler never calls completeMetadata() then
> > validMetadata == request->metadata(). So that applications will
> > receive the same metadata buffer in metadataCompleted and
> > requestCompleted. I wonder if we should emit the metadataCompleted
> > signal at all if the pipeline handler never calls completeMetadata()
> > (it would be easy to check, just make sure that Request:completedMetadata_
> > is empty with an helper function).
>
> I think this is how we expect a pipeline handler to behave and how an
> application to handle signals.

Do you expect all pipelines to use the new signal ?

I quickly checked rkisp1 where most metadata are computed by the IPA
in one go after parsing statistics. Only the frame timestamp and the
scaler crop are available at different times (the buffer completion,
so possibily later than the IPA computed ones).

>
> In the current implementation, it's safer for applications that whether
> a pipeline handler calls completeMetadata, applications still get full
> metadata set with the new signal, and they don't need to rely on
> signal requestCompleted. In other words, I want to guarantee that
> applications can choose to use either metadataAvailable or
> requestCompleted.
> Triggering the new signal if there's any metadata left is just a safer
> option to support all pipeline handlers.

My concern is about sending over a signal a possibly long list of
controls two times in the case all metadata are sent in a single chunk
at requestComplete() time.

>
> Of course we can choose to check if
> `Request::Private::completedMetadata_` is empty, and only handle
> this case, and expect the proper implementations of pipeline
> handlers call completeMetadata properly without missing any
> metadata. I just feel that it's unnecessary.
> Also, I'm not sure if there's a case that pipeline handler doesn't
> want to report some metadata earlier, and would rather send
> them altogether when a request is completed. In this case,
> your approach would require a pipeline handler to record
> those unsent metadata on its own.
>
> WDYT?
>
> >
> > > >
> > > > - If a pipeline handler doesn't call PipelineHandler::completeMetadata()
> > > >   the full metadata buffer will be in Request::metadata().
> > > >
> > > > How does an application know what to expect ?
> > > >
> > > > Imo we should advertise if the camera supports partial metadata or not
> > > > (maybe through a property). If it does applications should opt-in to
> > > > receive partial metadata. This could even be done per-configuration by
> > > > adding a field to CameraConfiguration ?
> > > >
> > > > If applications enable early metdata completion they agree to handle
> > > > the new signal, and the behaviour will be the one implemented in this
> > > > patch. If they do not enable early metadata completion then
> > > > PipelineHandler::completeMetadata() should simply merge the partial
> > > > metadata in Request::metadata() and not emit the new signal but return
> > > > the full metadata buffer as part of the Request in requestCompleted,
> > > > as they currently do so that pipelines that use early completion and
> > > > pipelines that do not will behave identically from an application
> > > > perspective.
> > > >
> > > > What do you think ?
> > > >
> > > > > + */
> > > > > +
> > > > >  /**
> > > > >   * \var Camera::requestCompleted
> > > > >   * \brief Signal emitted when a request queued to the camera has completed
> > > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > > > index e5940469..5d2999cb 100644
> > > > > --- a/src/libcamera/pipeline_handler.cpp
> > > > > +++ b/src/libcamera/pipeline_handler.cpp
> > > > > @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
> > > > >       return request->_d()->completeBuffer(buffer);
> > > > >  }
> > > > >
> > > > > +/**
> > > > > + * \brief Complete part of metadata for a request
> > > > > + * \param[in] request The request the buffer belongs to
> > > >
> > > >     * \param[in] request The Request the metadata buffer belongs to
> > >
> > > Perhaps:
> > > `* \param[in] request The Request the metadata belongs to`
> > > ?
> > >
> >
> > ack
> >
> > > >
> > > >
> > > > > + * \param[in] metadata The partial metadata that has completed
> > > > > + *
> > > > > + * This function could be called by pipeline handlers to signal completion of
> > > > > + * the \a metadata part of the \a request. It notifies applications of metadata
> > > > > + * completion.
> > > >
> > > > I think a little more details are needed.
> > > >
> > > >     * This function could be called by pipeline handlers to signal
> > > >     * availability of \a metadata before \a request completes. Early
> > > >     * metadata completion allows to notify applications about the
> > > >     * availability of a partial metadata buffer before the associated
> > > >     * Request has completed.
> > >
> > > Thanks! Updated.
> > >
> > > >
> > > > 1) According to this implementation metadata can be completed
> > > > out-of-order (partial metadata for Request X+1 can be signalled before
> > > > Request X completes). Is this desirable ?
> > >
> > > Yes, this is what we expect, and Android also allows that.
> > > WDYT?
> > >
> >
> > I'll ask others what to they think
> >
> > > >
> > > > > + *
> > > > > + * \context This function shall be called from the CameraManager thread.
> > > > > + */
> > > > > +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> > > > > +{
> > > > > +     const ControlList &validMetadata = request->addCompletedMetadata(metadata);
> > > >
> > > > Request::addCompleteMetadata constructs and returns a ControlList,
> > > > I'm surprised the compiler doesn't complain you're taking a reference
> > > > to a temporary object (it's apparently legal for const references only).
> > > >
> > > > However, as a temporary will be constructed anyway this doesn't
> > > > buy you any gain, possibily the contrary, as this will disallow C++
> > > > copy elision from happening (afaiu). I think you should drop &
> > >
> > > Right, it's definitely a mistake. Removed.
> > >
> > > >
> > > >
> > > > > +     if (!validMetadata.empty()) {
> > > > > +             request->metadata().merge(validMetadata);
> > > > > +
> > > > > +             Camera *camera = request->_d()->camera();
> > > > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > > > +     }
> > > > > +}
> > > > > +
> > > > >  /**
> > > > >   * \brief Signal request completion
> > > > >   * \param[in] request The request that has completed
> > > > > @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
> > > > >
> > > > >       Camera::Private *data = camera->_d();
> > > > >
> > > > > +     /*
> > > > > +      * Collect metadata which is not yet completed by the Camera, and
> > > > > +      * create one partial result to cover the missing metadata before
> > > > > +      * completing the whole request. This guarantees the aggregation of
> > > > > +      * metadata in completed partial results equals to the global metadata
> > > > > +      * in the request.
> > > > > +      *
> > > > > +      * \todo: Forbid merging metadata into request.metadata() directly and
> > > > > +      * force calling completeMetadata() to report metadata.
> > > > > +      */
> >
> > Could you please clarify me what this \todo means ?
>
> Similar to the above reasons:
> Currently all pipeline handlers merge metadata into request.metadata()
> directly, except for the upcoming mtkisp7. We were expecting every
> pipeline handler to migrate to `PipelineHandler::completeMetadata`
> eventually.
>
> Let me know if you think this todo doesn't make sense, as we should
> still allow the previous way to merge into request.metadata() directly,
> or there's a case that a pipeline handler doesn't want to report some
> metadata earlier.
>

I'm just not 100% sure all pipelines will be capable of actually send
metadata earlier. True, in the RkISP1 case I used as an example as
soon as the IPA has processed stats and computed metadata the pipeline
can call metadataComplete() but we have no guarantee the application
handles the signal, so we end up sending the same metadata list twice
(one in chunks, the other as Request::metadata() at requestComplete()
time.

I'll check with others what are the implications in terms of
performances of this.

Thanks
  j

> >
> > > > > +     const ControlList &validMetadata = request->addCompletedMetadata(
> > > > > +             request->metadata());
> > > > > +     if (!validMetadata.empty())
> > > > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > > > +
> > > > >       while (!data->queuedRequests_.empty()) {
> > > > >               Request *req = data->queuedRequests_.front();
> > > > >               if (req->status() == Request::RequestPending)
> > > > > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> > > > > index 8c56ed30..3ce23a8e 100644
> > > > > --- a/src/libcamera/request.cpp
> > > > > +++ b/src/libcamera/request.cpp
> > > > > @@ -598,6 +598,26 @@ std::string Request::toString() const
> > > > >       return ss.str();
> > > > >  }
> > > > >
> > > > > +/**
> > > > > + * \brief Add completed metadata, as a partial result
> > > > > + * \param[in] metadata The metadata completed
> > > > > + *
> > > > > + * Request will record the entries that has been sent to the application, to
> > > > > + * prevent duplicated controls.
> > > > > + *
> > > > > + * \return ControlList that hasn't been completed before
> > > > > + */
> > > > > +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> > > > > +{
> > > > > +     ControlList resultMetadata;
> > > > > +     for (auto &[id, value] : metadata) {
> > > > > +             if (!completedMetadata_.count(id))
> > > >
> > > > completedMetadata_ should be cleared in Request::reset().
> > >
> > > Nice catch! Then how about we move the member variable and the member
> > > function to `Request::Private` instead?
> > >
> >
> > Yes, they both do not need to be exposed to applications indeed.
> >
> > > >
> > > > I wonder if we should actually prevent the same metadata to be
> > > > returned multiple times by the pipeline handler or not.
> > >
> > > This is actually prohibited by Android camera hal [1]:
> > > `Partial metadata submitted should not include any metadata key
> > > returned in a previous partial result for a given frame.`
> > >
> > > We can certainly prevent that in Android Adapter only, while I think
> > > having this restriction in libcamera core library also makes sense.
> > > WDYT? I can also update the description of the new signal.
> > >
> > > [1]: https://source.android.com/reference/hal/structcamera3__capture__result#:~:text=partial_result-,Detailed%20Description,the%20result%20metadata%20to%20NULL.
> >
> > I can't find in the text where this is forbidden, however I'm not
> > pushing for the other option, I think it's actually saner to only
> > signal a metadata once per Request. I wonder however if this shouldn't
> > be a WARN as it means the pipeline handler is doing something
> > unexpected.
>
> True that a WARN is an option.
> I would prefer the current approach though, otherwise I need to
> implement the logic in Android Adapter instead.
>
> Please let me know if anyone insists on the other approach.
>
> > Also, the expectations that a metadata is returned once
> > per Request has to be documented in the documentation of
> > PipelineHandler::completeMetadata()
>
> I'll update the doc in the next version :)
>
> BR,
> Harvey
>
> >
> > Thanks
> >   j
> >
> > >
> > > BR,
> > > Harvey
> > > >
> > > > > +                     resultMetadata.set(id, value);
> > > > > +     }
> > > > > +
> > > > > +     return resultMetadata;
> > > > > +}
> > > > > +
> > > > >  /**
> > > > >   * \brief Insert a text representation of a Request into an output stream
> > > > >   * \param[in] out The output stream
> > > > > --
> > > > > 2.46.0.598.g6f2099f65c-goog
> > > > >
Cheng-Hao Yang Nov. 14, 2024, 11:06 a.m. UTC | #7
Hi Jacopo,

On Thu, Nov 14, 2024 at 6:17 PM Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Harvey
>
> On Thu, Nov 14, 2024 at 05:11:42PM +0800, Cheng-Hao Yang wrote:
> > Hi Jacopo,
> >
> > On Thu, Nov 14, 2024 at 4:36 PM Jacopo Mondi
> > <jacopo.mondi@ideasonboard.com> wrote:
> > >
> > > Hi Harvey
> > >
> > > On Thu, Nov 14, 2024 at 02:29:20PM +0800, Cheng-Hao Yang wrote:
> > > > Hi Jacopo,
> > > >
> > > > I'll upload a new version when all discussion is done.
> > > >
> > > > On Wed, Nov 13, 2024 at 6:47 PM Jacopo Mondi
> > > > <jacopo.mondi@ideasonboard.com> wrote:
> > > > >
> > > > > Hi Harvey,
> > > > >    sorry for the late feedback
> > > > >
> > > > > On Thu, Sep 12, 2024 at 04:52:48AM +0000, Harvey Yang wrote:
> > > > > > From: Han-Lin Chen <hanlinchen@chromium.org>
> > > > > >
> > > > > > Allows pipeline handler to signal metadata completion by adding the
> > > > > > following signals to Camera:
> > > > > >
> > > > > > Signal<Request *, const ControlList &> metadataCompleted;
> > > > >
> > > > > Bikeshedding on the name a bit, as this is allowed to be called
> > > > > multiple times, with partial metadata, I would call it
> > > > > 'metadataAvailable'.
> > > >
> > > > Sure, 'metadataAvailable' does make more sense.
> > > > Thanks!
> > > >
> > > > >
> > > > > >
> > > > > > Together with the bufferCompleted signal, the pipeline handler is allowed to
> > > > > > return buffers and partial metadata at any stage of processing.
> > > > > >
> > > > > > Signed-off-by: Han-Lin Chen <hanlinchen@chromium.org>
> > > > > > Co-developed-by: Harvey Yang <chenghaoyang@chromium.org>
> > > > > > ---
> > > > > >  include/libcamera/camera.h                    |  1 +
> > > > > >  include/libcamera/internal/pipeline_handler.h |  1 +
> > > > > >  include/libcamera/request.h                   |  5 +++
> > > > > >  src/libcamera/camera.cpp                      |  6 +++
> > > > > >  src/libcamera/pipeline_handler.cpp            | 37 +++++++++++++++++++
> > > > > >  src/libcamera/request.cpp                     | 20 ++++++++++
> > > > > >  6 files changed, 70 insertions(+)
> > > > > >
> > > > > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
> > > > > > index 94cee7bd..08c5c58c 100644
> > > > > > --- a/include/libcamera/camera.h
> > > > > > +++ b/include/libcamera/camera.h
> > > > > > @@ -122,6 +122,7 @@ public:
> > > > > >
> > > > > >       const std::string &id() const;
> > > > > >
> > > > > > +     Signal<Request *, const ControlList &> metadataCompleted;
> > > > > >       Signal<Request *, FrameBuffer *> bufferCompleted;
> > > > > >       Signal<Request *> requestCompleted;
> > > > > >       Signal<> disconnected;
> > > > > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > > > > > index 0d380803..1af63a23 100644
> > > > > > --- a/include/libcamera/internal/pipeline_handler.h
> > > > > > +++ b/include/libcamera/internal/pipeline_handler.h
> > > > > > @@ -58,6 +58,7 @@ public:
> > > > > >       void registerRequest(Request *request);
> > > > > >       void queueRequest(Request *request);
> > > > > >
> > > > > > +     void completeMetadata(Request *request, const ControlList &metadata);
> > > > > >       bool completeBuffer(Request *request, FrameBuffer *buffer);
> > > > > >       void completeRequest(Request *request);
> > > > > >
> > > > > > diff --git a/include/libcamera/request.h b/include/libcamera/request.h
> > > > > > index e214a9d1..0200f4a1 100644
> > > > > > --- a/include/libcamera/request.h
> > > > > > +++ b/include/libcamera/request.h
> > > > > > @@ -12,6 +12,7 @@
> > > > > >  #include <ostream>
> > > > > >  #include <stdint.h>
> > > > > >  #include <string>
> > > > > > +#include <unordered_set>
> > > > > >
> > > > > >  #include <libcamera/base/class.h>
> > > > > >  #include <libcamera/base/signal.h>
> > > > > > @@ -64,6 +65,8 @@ public:
> > > > > >
> > > > > >       std::string toString() const;
> > > > > >
> > > > > > +     ControlList addCompletedMetadata(const ControlList &metadata);
> > > > > > +
> > > > > >  private:
> > > > > >       LIBCAMERA_DISABLE_COPY(Request)
> > > > > >
> > > > > > @@ -73,6 +76,8 @@ private:
> > > > > >
> > > > > >       const uint64_t cookie_;
> > > > > >       Status status_;
> > > > > > +
> > > > > > +     std::unordered_set<unsigned int> completedMetadata_;
> > > > > >  };
> > > > > >
> > > > > >  std::ostream &operator<<(std::ostream &out, const Request &r);
> > > > > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
> > > > > > index a86f552a..5ffae23e 100644
> > > > > > --- a/src/libcamera/camera.cpp
> > > > > > +++ b/src/libcamera/camera.cpp
> > > > > > @@ -892,6 +892,12 @@ const std::string &Camera::id() const
> > > > > >   * completed
> > > > > >   */
> > > > > >
> > > > > > +/**
> > > > > > + * \var Camera::metadataCompleted
> > > > > > + * \brief Signal emitted when some metadata for a request is completed as a
> > > > > > + * partial result
> > > > >
> > > > > Currently, we return metadata as part of a Request signalled with
> > > > > Camera::requestCompleted. Existing applications do not handle the new
> > > > > signal and I don't think we should make it mandatory to do so.
> > > > > According to the implementation of this patch, the same application
> > > > > running on a pipeline handler that calls
> > > > > PipelineHandler::completeMetadata and on a pipeline handler that
> > > > > doesn't will receive different sets of metadata.
> > > > >
> > > > > - If a pipeline handler calls PipelineHandler::completeMetadata() it
> > > > >   will send metadata in chunks and only the ones not previously
> > > > >   signalled with be part of Request::metadata().
> > > >
> > > > Actually, it's not true.
> > > > In this patch, `PipelineHandler::completeMetadata` will merge the
> > > > partially available metadata into Request:
> > > > `request->metadata().merge(validMetadata);`
> > > >
> > >
> > > Ups, sorry I missed it!
> > >
> > > > Therefore, whether an application handles the new signal, it can
> > > > still get access to all the metadata in a request via
> > > > `Request.metadata()`. This should ease your concern of the old
> > > > and new implementations of applications.
> > > >
> > >
> > > Yes, if this time I got this right, the full metadata pack will be
> > > available in Request::metadata() at requestCompleted time, but it will
> > > be completed in chunks if the pipeline handler calls
> > > completeMetadata() earlier.
> > >
> > > There's only one thing left that I'm not sure about: in
> > > requestCompleted()
> > >
> > >         const ControlList &validMetadata = request->addCompletedMetadata(
> > >                 request->metadata());
> > >         if (!validMetadata.empty())
> > >                 camera->metadataCompleted.emit(request, validMetadata);
> > >
> > > If the pipeline handler never calls completeMetadata() then
> > > validMetadata == request->metadata(). So that applications will
> > > receive the same metadata buffer in metadataCompleted and
> > > requestCompleted. I wonder if we should emit the metadataCompleted
> > > signal at all if the pipeline handler never calls completeMetadata()
> > > (it would be easy to check, just make sure that Request:completedMetadata_
> > > is empty with an helper function).
> >
> > I think this is how we expect a pipeline handler to behave and how an
> > application to handle signals.
>
> Do you expect all pipelines to use the new signal ?

Not really, while I still think that we need to ensure the new signal sends
a complete set of metadata keys.

>
> I quickly checked rkisp1 where most metadata are computed by the IPA
> in one go after parsing statistics. Only the frame timestamp and the
> scaler crop are available at different times (the buffer completion,
> so possibily later than the IPA computed ones).
>
> >
> > In the current implementation, it's safer for applications that whether
> > a pipeline handler calls completeMetadata, applications still get full
> > metadata set with the new signal, and they don't need to rely on
> > signal requestCompleted. In other words, I want to guarantee that
> > applications can choose to use either metadataAvailable or
> > requestCompleted.
> > Triggering the new signal if there's any metadata left is just a safer
> > option to support all pipeline handlers.
>
> My concern is about sending over a signal a possibly long list of
> controls two times in the case all metadata are sent in a single chunk
> at requestComplete() time.
>
> >
> > Of course we can choose to check if
> > `Request::Private::completedMetadata_` is empty, and only handle
> > this case, and expect the proper implementations of pipeline
> > handlers call completeMetadata properly without missing any
> > metadata. I just feel that it's unnecessary.
> > Also, I'm not sure if there's a case that pipeline handler doesn't
> > want to report some metadata earlier, and would rather send
> > them altogether when a request is completed. In this case,
> > your approach would require a pipeline handler to record
> > those unsent metadata on its own.
> >
> > WDYT?
> >
> > >
> > > > >
> > > > > - If a pipeline handler doesn't call PipelineHandler::completeMetadata()
> > > > >   the full metadata buffer will be in Request::metadata().
> > > > >
> > > > > How does an application know what to expect ?
> > > > >
> > > > > Imo we should advertise if the camera supports partial metadata or not
> > > > > (maybe through a property). If it does applications should opt-in to
> > > > > receive partial metadata. This could even be done per-configuration by
> > > > > adding a field to CameraConfiguration ?
> > > > >
> > > > > If applications enable early metdata completion they agree to handle
> > > > > the new signal, and the behaviour will be the one implemented in this
> > > > > patch. If they do not enable early metadata completion then
> > > > > PipelineHandler::completeMetadata() should simply merge the partial
> > > > > metadata in Request::metadata() and not emit the new signal but return
> > > > > the full metadata buffer as part of the Request in requestCompleted,
> > > > > as they currently do so that pipelines that use early completion and
> > > > > pipelines that do not will behave identically from an application
> > > > > perspective.
> > > > >
> > > > > What do you think ?
> > > > >
> > > > > > + */
> > > > > > +
> > > > > >  /**
> > > > > >   * \var Camera::requestCompleted
> > > > > >   * \brief Signal emitted when a request queued to the camera has completed
> > > > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > > > > index e5940469..5d2999cb 100644
> > > > > > --- a/src/libcamera/pipeline_handler.cpp
> > > > > > +++ b/src/libcamera/pipeline_handler.cpp
> > > > > > @@ -535,6 +535,28 @@ bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
> > > > > >       return request->_d()->completeBuffer(buffer);
> > > > > >  }
> > > > > >
> > > > > > +/**
> > > > > > + * \brief Complete part of metadata for a request
> > > > > > + * \param[in] request The request the buffer belongs to
> > > > >
> > > > >     * \param[in] request The Request the metadata buffer belongs to
> > > >
> > > > Perhaps:
> > > > `* \param[in] request The Request the metadata belongs to`
> > > > ?
> > > >
> > >
> > > ack
> > >
> > > > >
> > > > >
> > > > > > + * \param[in] metadata The partial metadata that has completed
> > > > > > + *
> > > > > > + * This function could be called by pipeline handlers to signal completion of
> > > > > > + * the \a metadata part of the \a request. It notifies applications of metadata
> > > > > > + * completion.
> > > > >
> > > > > I think a little more details are needed.
> > > > >
> > > > >     * This function could be called by pipeline handlers to signal
> > > > >     * availability of \a metadata before \a request completes. Early
> > > > >     * metadata completion allows to notify applications about the
> > > > >     * availability of a partial metadata buffer before the associated
> > > > >     * Request has completed.
> > > >
> > > > Thanks! Updated.
> > > >
> > > > >
> > > > > 1) According to this implementation metadata can be completed
> > > > > out-of-order (partial metadata for Request X+1 can be signalled before
> > > > > Request X completes). Is this desirable ?
> > > >
> > > > Yes, this is what we expect, and Android also allows that.
> > > > WDYT?
> > > >
> > >
> > > I'll ask others what to they think
> > >
> > > > >
> > > > > > + *
> > > > > > + * \context This function shall be called from the CameraManager thread.
> > > > > > + */
> > > > > > +void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
> > > > > > +{
> > > > > > +     const ControlList &validMetadata = request->addCompletedMetadata(metadata);
> > > > >
> > > > > Request::addCompleteMetadata constructs and returns a ControlList,
> > > > > I'm surprised the compiler doesn't complain you're taking a reference
> > > > > to a temporary object (it's apparently legal for const references only).
> > > > >
> > > > > However, as a temporary will be constructed anyway this doesn't
> > > > > buy you any gain, possibily the contrary, as this will disallow C++
> > > > > copy elision from happening (afaiu). I think you should drop &
> > > >
> > > > Right, it's definitely a mistake. Removed.
> > > >
> > > > >
> > > > >
> > > > > > +     if (!validMetadata.empty()) {
> > > > > > +             request->metadata().merge(validMetadata);
> > > > > > +
> > > > > > +             Camera *camera = request->_d()->camera();
> > > > > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > > > > +     }
> > > > > > +}
> > > > > > +
> > > > > >  /**
> > > > > >   * \brief Signal request completion
> > > > > >   * \param[in] request The request that has completed
> > > > > > @@ -557,6 +579,21 @@ void PipelineHandler::completeRequest(Request *request)
> > > > > >
> > > > > >       Camera::Private *data = camera->_d();
> > > > > >
> > > > > > +     /*
> > > > > > +      * Collect metadata which is not yet completed by the Camera, and
> > > > > > +      * create one partial result to cover the missing metadata before
> > > > > > +      * completing the whole request. This guarantees the aggregation of
> > > > > > +      * metadata in completed partial results equals to the global metadata
> > > > > > +      * in the request.
> > > > > > +      *
> > > > > > +      * \todo: Forbid merging metadata into request.metadata() directly and
> > > > > > +      * force calling completeMetadata() to report metadata.
> > > > > > +      */
> > >
> > > Could you please clarify me what this \todo means ?
> >
> > Similar to the above reasons:
> > Currently all pipeline handlers merge metadata into request.metadata()
> > directly, except for the upcoming mtkisp7. We were expecting every
> > pipeline handler to migrate to `PipelineHandler::completeMetadata`
> > eventually.
> >
> > Let me know if you think this todo doesn't make sense, as we should
> > still allow the previous way to merge into request.metadata() directly,
> > or there's a case that a pipeline handler doesn't want to report some
> > metadata earlier.
> >
>
> I'm just not 100% sure all pipelines will be capable of actually send
> metadata earlier. True, in the RkISP1 case I used as an example as
> soon as the IPA has processed stats and computed metadata the pipeline
> can call metadataComplete() but we have no guarantee the application
> handles the signal, so we end up sending the same metadata list twice
> (one in chunks, the other as Request::metadata() at requestComplete()
> time.

I understand your concern, while `libcamera::Signal` doesn't do anything
if there's no slot connected. We still prepare the data chunks though.
We can also check if `libcamera::Signal::slots()` is empty before preparing
the data to be sent. WDYT?

BR,
Harvey

>
> I'll check with others what are the implications in terms of
> performances of this.
>
> Thanks
>   j
>
> > >
> > > > > > +     const ControlList &validMetadata = request->addCompletedMetadata(
> > > > > > +             request->metadata());
> > > > > > +     if (!validMetadata.empty())
> > > > > > +             camera->metadataCompleted.emit(request, validMetadata);
> > > > > > +
> > > > > >       while (!data->queuedRequests_.empty()) {
> > > > > >               Request *req = data->queuedRequests_.front();
> > > > > >               if (req->status() == Request::RequestPending)
> > > > > > diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
> > > > > > index 8c56ed30..3ce23a8e 100644
> > > > > > --- a/src/libcamera/request.cpp
> > > > > > +++ b/src/libcamera/request.cpp
> > > > > > @@ -598,6 +598,26 @@ std::string Request::toString() const
> > > > > >       return ss.str();
> > > > > >  }
> > > > > >
> > > > > > +/**
> > > > > > + * \brief Add completed metadata, as a partial result
> > > > > > + * \param[in] metadata The metadata completed
> > > > > > + *
> > > > > > + * Request will record the entries that has been sent to the application, to
> > > > > > + * prevent duplicated controls.
> > > > > > + *
> > > > > > + * \return ControlList that hasn't been completed before
> > > > > > + */
> > > > > > +ControlList Request::addCompletedMetadata(const ControlList &metadata)
> > > > > > +{
> > > > > > +     ControlList resultMetadata;
> > > > > > +     for (auto &[id, value] : metadata) {
> > > > > > +             if (!completedMetadata_.count(id))
> > > > >
> > > > > completedMetadata_ should be cleared in Request::reset().
> > > >
> > > > Nice catch! Then how about we move the member variable and the member
> > > > function to `Request::Private` instead?
> > > >
> > >
> > > Yes, they both do not need to be exposed to applications indeed.
> > >
> > > > >
> > > > > I wonder if we should actually prevent the same metadata to be
> > > > > returned multiple times by the pipeline handler or not.
> > > >
> > > > This is actually prohibited by Android camera hal [1]:
> > > > `Partial metadata submitted should not include any metadata key
> > > > returned in a previous partial result for a given frame.`
> > > >
> > > > We can certainly prevent that in Android Adapter only, while I think
> > > > having this restriction in libcamera core library also makes sense.
> > > > WDYT? I can also update the description of the new signal.
> > > >
> > > > [1]: https://source.android.com/reference/hal/structcamera3__capture__result#:~:text=partial_result-,Detailed%20Description,the%20result%20metadata%20to%20NULL.
> > >
> > > I can't find in the text where this is forbidden, however I'm not
> > > pushing for the other option, I think it's actually saner to only
> > > signal a metadata once per Request. I wonder however if this shouldn't
> > > be a WARN as it means the pipeline handler is doing something
> > > unexpected.
> >
> > True that a WARN is an option.
> > I would prefer the current approach though, otherwise I need to
> > implement the logic in Android Adapter instead.
> >
> > Please let me know if anyone insists on the other approach.
> >
> > > Also, the expectations that a metadata is returned once
> > > per Request has to be documented in the documentation of
> > > PipelineHandler::completeMetadata()
> >
> > I'll update the doc in the next version :)
> >
> > BR,
> > Harvey
> >
> > >
> > > Thanks
> > >   j
> > >
> > > >
> > > > BR,
> > > > Harvey
> > > > >
> > > > > > +                     resultMetadata.set(id, value);
> > > > > > +     }
> > > > > > +
> > > > > > +     return resultMetadata;
> > > > > > +}
> > > > > > +
> > > > > >  /**
> > > > > >   * \brief Insert a text representation of a Request into an output stream
> > > > > >   * \param[in] out The output stream
> > > > > > --
> > > > > > 2.46.0.598.g6f2099f65c-goog
> > > > > >

Patch
diff mbox series

diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 94cee7bd..08c5c58c 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -122,6 +122,7 @@  public:
 
 	const std::string &id() const;
 
+	Signal<Request *, const ControlList &> metadataCompleted;
 	Signal<Request *, FrameBuffer *> bufferCompleted;
 	Signal<Request *> requestCompleted;
 	Signal<> disconnected;
diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index 0d380803..1af63a23 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -58,6 +58,7 @@  public:
 	void registerRequest(Request *request);
 	void queueRequest(Request *request);
 
+	void completeMetadata(Request *request, const ControlList &metadata);
 	bool completeBuffer(Request *request, FrameBuffer *buffer);
 	void completeRequest(Request *request);
 
diff --git a/include/libcamera/request.h b/include/libcamera/request.h
index e214a9d1..0200f4a1 100644
--- a/include/libcamera/request.h
+++ b/include/libcamera/request.h
@@ -12,6 +12,7 @@ 
 #include <ostream>
 #include <stdint.h>
 #include <string>
+#include <unordered_set>
 
 #include <libcamera/base/class.h>
 #include <libcamera/base/signal.h>
@@ -64,6 +65,8 @@  public:
 
 	std::string toString() const;
 
+	ControlList addCompletedMetadata(const ControlList &metadata);
+
 private:
 	LIBCAMERA_DISABLE_COPY(Request)
 
@@ -73,6 +76,8 @@  private:
 
 	const uint64_t cookie_;
 	Status status_;
+
+	std::unordered_set<unsigned int> completedMetadata_;
 };
 
 std::ostream &operator<<(std::ostream &out, const Request &r);
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index a86f552a..5ffae23e 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -892,6 +892,12 @@  const std::string &Camera::id() const
  * completed
  */
 
+/**
+ * \var Camera::metadataCompleted
+ * \brief Signal emitted when some metadata for a request is completed as a
+ * partial result
+ */
+
 /**
  * \var Camera::requestCompleted
  * \brief Signal emitted when a request queued to the camera has completed
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index e5940469..5d2999cb 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -535,6 +535,28 @@  bool PipelineHandler::completeBuffer(Request *request, FrameBuffer *buffer)
 	return request->_d()->completeBuffer(buffer);
 }
 
+/**
+ * \brief Complete part of metadata for a request
+ * \param[in] request The request the buffer belongs to
+ * \param[in] metadata The partial metadata that has completed
+ *
+ * This function could be called by pipeline handlers to signal completion of
+ * the \a metadata part of the \a request. It notifies applications of metadata
+ * completion.
+ *
+ * \context This function shall be called from the CameraManager thread.
+ */
+void PipelineHandler::completeMetadata(Request *request, const ControlList &metadata)
+{
+	const ControlList &validMetadata = request->addCompletedMetadata(metadata);
+	if (!validMetadata.empty()) {
+		request->metadata().merge(validMetadata);
+
+		Camera *camera = request->_d()->camera();
+		camera->metadataCompleted.emit(request, validMetadata);
+	}
+}
+
 /**
  * \brief Signal request completion
  * \param[in] request The request that has completed
@@ -557,6 +579,21 @@  void PipelineHandler::completeRequest(Request *request)
 
 	Camera::Private *data = camera->_d();
 
+	/*
+	 * Collect metadata which is not yet completed by the Camera, and
+	 * create one partial result to cover the missing metadata before
+	 * completing the whole request. This guarantees the aggregation of
+	 * metadata in completed partial results equals to the global metadata
+	 * in the request.
+	 *
+	 * \todo: Forbid merging metadata into request.metadata() directly and
+	 * force calling completeMetadata() to report metadata.
+	 */
+	const ControlList &validMetadata = request->addCompletedMetadata(
+		request->metadata());
+	if (!validMetadata.empty())
+		camera->metadataCompleted.emit(request, validMetadata);
+
 	while (!data->queuedRequests_.empty()) {
 		Request *req = data->queuedRequests_.front();
 		if (req->status() == Request::RequestPending)
diff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp
index 8c56ed30..3ce23a8e 100644
--- a/src/libcamera/request.cpp
+++ b/src/libcamera/request.cpp
@@ -598,6 +598,26 @@  std::string Request::toString() const
 	return ss.str();
 }
 
+/**
+ * \brief Add completed metadata, as a partial result
+ * \param[in] metadata The metadata completed
+ *
+ * Request will record the entries that has been sent to the application, to
+ * prevent duplicated controls.
+ *
+ * \return ControlList that hasn't been completed before
+ */
+ControlList Request::addCompletedMetadata(const ControlList &metadata)
+{
+	ControlList resultMetadata;
+	for (auto &[id, value] : metadata) {
+		if (!completedMetadata_.count(id))
+			resultMetadata.set(id, value);
+	}
+
+	return resultMetadata;
+}
+
 /**
  * \brief Insert a text representation of a Request into an output stream
  * \param[in] out The output stream