[RFC,v2,16/22] libcamera: pipeline_handler: Add metadataAvailable() function
diff mbox series

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

Commit Message

Barnabás Pőcze July 21, 2025, 10:46 a.m. UTC
From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>

Currently the way pipeline handlers have to store metadata results
for a Request is to access the Request::metadata_ list directly and
either merge a ControlList or set a single control value there.

Direct access to Request::metadata_ is however problematic as even if
metadata would be available earlier, pipeline handlers can only
accumulate the results in Request::metadata_ and they're only available
for applications at Request complete time.

Instead of letting pipeline handlers access Request::metadata_ directly
provide two helper functions, similar in spirit to
PipelineHandler::completeBuffer() and
PipelineHandler::completeRequest(), to allow pipeline handlers to
notify early availability of metadata.

Provide three overloads, one that accepts a ControlList and merges it into
Request::metadata_, one that allows to set a single metadata result there
without going through an intermediate copy, and one that runs a callback
for conditional metadata reporting without intermediate an ControlList.

The newly provided helpers trigger the Camera::availableMetadata signal
from where applications can retrieve the list of controls that have
just been made available by the pipeline handler.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
[Fill both lists, use `type_identity`, new overload, adjust commit message.]
Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
Original: https://patchwork.libcamera.org/patch/22229/

changes in v2:
  * add new overload that takes an invocable object for more flexibility
---
 include/libcamera/internal/pipeline_handler.h | 49 +++++++++++
 src/libcamera/pipeline_handler.cpp            | 84 +++++++++++++++++++
 2 files changed, 133 insertions(+)

Comments

Jacopo Mondi July 28, 2025, 10:08 a.m. UTC | #1
Hi Barnabás

On Mon, Jul 21, 2025 at 12:46:16PM +0200, Barnabás Pőcze wrote:
> From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>
> Currently the way pipeline handlers have to store metadata results
> for a Request is to access the Request::metadata_ list directly and
> either merge a ControlList or set a single control value there.
>
> Direct access to Request::metadata_ is however problematic as even if
> metadata would be available earlier, pipeline handlers can only
> accumulate the results in Request::metadata_ and they're only available
> for applications at Request complete time.
>
> Instead of letting pipeline handlers access Request::metadata_ directly
> provide two helper functions, similar in spirit to
> PipelineHandler::completeBuffer() and
> PipelineHandler::completeRequest(), to allow pipeline handlers to
> notify early availability of metadata.
>
> Provide three overloads, one that accepts a ControlList and merges it into
> Request::metadata_, one that allows to set a single metadata result there
> without going through an intermediate copy, and one that runs a callback
> for conditional metadata reporting without intermediate an ControlList.
>
> The newly provided helpers trigger the Camera::availableMetadata signal
> from where applications can retrieve the list of controls that have
> just been made available by the pipeline handler.
>
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> [Fill both lists, use `type_identity`, new overload, adjust commit message.]
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
> Original: https://patchwork.libcamera.org/patch/22229/
>
> changes in v2:
>   * add new overload that takes an invocable object for more flexibility
> ---
>  include/libcamera/internal/pipeline_handler.h | 49 +++++++++++
>  src/libcamera/pipeline_handler.cpp            | 84 +++++++++++++++++++
>  2 files changed, 133 insertions(+)
>
> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> index e89d6a33e..49c907178 100644
> --- a/include/libcamera/internal/pipeline_handler.h
> +++ b/include/libcamera/internal/pipeline_handler.h
> @@ -7,16 +7,21 @@
>
>  #pragma once
>
> +#include <functional>
>  #include <memory>
>  #include <string>
>  #include <sys/types.h>
>  #include <vector>
>
> +#include <libcamera/base/details/cxx20.h>
>  #include <libcamera/base/object.h>
>
> +#include <libcamera/camera.h>
>  #include <libcamera/controls.h>
>  #include <libcamera/stream.h>
>
> +#include "libcamera/internal/request.h"
> +
>  namespace libcamera {
>
>  class Camera;
> @@ -58,6 +63,50 @@ public:
>  	void registerRequest(Request *request);
>  	void queueRequest(Request *request);
>
> +	void metadataAvailable(Request *request, const ControlList &metadata);
> +
> +	template<typename T>
> +	void metadataAvailable(Request *request, const Control<T> &ctrl,
> +			       const details::cxx20::type_identity_t<T> &value)

I presume the usage of type_identity_t<T> is a safety measure to avoid
a deduction failure. However I presume T is fully specified  and the
type of the last argument should match the Control<T> one.

Howver I presume this doesn't hurt, and might save some deduction
failure ?

> +	{
> +		auto &m = request->metadata2();
> +		const auto c = m.checkpoint();
> +
> +		m.set(ctrl, value);
> +		request->metadata().set(ctrl, value);
> +
> +		const auto d = c.diffSince();
> +		if (d)
> +			request->_d()->camera()->metadataAvailable.emit(request, d);
> +	}
> +
> +#ifndef __DOXYGEN__
> +	struct MetadataSetter {
> +		Request *request;
> +
> +		template<typename T>
> +		void operator()(const Control<T> &ctrl,
> +			        const details::cxx20::type_identity_t<T> &value) const
> +		{
> +			request->metadata().set(ctrl, value);
> +			request->metadata2().set(ctrl, value);
> +		}
> +	};
> +
> +	template<typename Func, std::enable_if_t<std::is_invocable_v<Func&, MetadataSetter>> * = nullptr>
> +#else
> +	template<typename Func>
> +#endif
> +	void metadataAvailable(Request *request, Func func)
> +	{
> +		const auto c = request->metadata2().checkpoint();
> +
> +		std::invoke(func, MetadataSetter{ request });
> +
> +		if (const auto d = c.diffSince())
> +			request->_d()->camera()->metadataAvailable.emit(request, d);
> +	}
> +
>  	bool completeBuffer(Request *request, FrameBuffer *buffer);
>  	void completeRequest(Request *request);
>  	void cancelRequest(Request *request);
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index e5f9e55c9..edfa9cf58 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -532,6 +532,90 @@ void PipelineHandler::doQueueRequests(Camera *camera)
>   * \return 0 on success or a negative error code otherwise
>   */
>
> +/**
> + * \brief Signal the availability of metadata for \a request
> + * \param[in] request The request the metadata belongs to
> + * \param[in] metadata The collection of metadata items
> + *
> + * This function copies metadata items from \a metadata to the cumulative metadata
> + * collection of \a request. This function may be called multiple times, but metadata
> + * items already present in Request::metadata() are ignored. Afterwards the function
> + * notifies the application by triggering the Camera::availableMetadata signal with
> + * the just added metadata items.
> + *
> + * Early metadata completion allows pipeline handlers to fast track delivery of
> + * metadata results as soon as they are available before the completion of \a
> + * request. The full list of metadata results of a Request is available at
> + * Request completion time in Request::metadata().
> + *
> + * \context This function shall be called from the CameraManager thread.
> + *
> + * \sa PipelineHandler::metadataAvailable(Request *request, Func func)
> + * \sa PipelineHandler::metadataAvailable(Request *request,
> + *                                        const Control<T> &ctrl,
> + *                                        const details::cxx20::type_identity_t<T> &value)
> + */
> +void PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> +{
> +	request->metadata().merge(metadata);
> +
> +	const auto d = request->metadata2().merge(metadata);
> +	if (d)
> +		request->_d()->camera()->metadataAvailable.emit(request, d);
> +}
> +
> +/**
> + * \fn void PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl,
> + *					       const details::cxx20::type_identity_t<T> &value)
> + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> + * \param[in] request The request the metadata belongs to
> + * \param[in] ctrl The control id of the metadata item
> + * \param[in] value The value of the metadata item
> + *
> + * This function servers the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)

you can break the line as

 * This function servers the same purpose as
 * PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)


> + * but it allows a single metadata item to be reported directly,
> + * without creating a ControlList.
> + *
> + * \context This function shall be called from the CameraManager thread.
> + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> + */
> +
> +/**
> + * \fn void PipelineHandler::metadataAvailable(Request *request, Func func)
> + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> + * \param[in] request The request the metadata belongs to
> + * \param[in] func The callback to invoke
> + *
> + * This function serves the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)

ditto

> + * but it provides more flexibility for pipeline handlers. This function invokes
> + * \a func, which receives as its sole argument an object of unspecified type whose
> + * operator() can be used to add metadata items.
> + *
> + * For example, a PipelineHandler might use this function to conditionally report two
> + * metadata items without creating an intermediate ControlList:
> + *
> + * \code
> +	metadataAvailable(request, [&](auto set) {
> +		if (x)
> +			set(controls::X, x);
> +		if (y)
> +			set(controls::Y, y);
> +		// ...
> +	});
> + * \endcode

Sorry I might be missing the use case here.

The call receives a MetadataSetter as 'set' (again auto requires a few
levels of indirection just to save a few characters), but I don't get
how the pipeline handler should call this, in particular what

        if (x)

        if (y)

might refer to...

What I mean is that pipeline handlers call metadataAvailable in
response to events, and they should provide a callback where they
inspect 'x' and 'y' which are conditions related to the event that
just happened ?

> + *
> + * The advantage of the above over two calls to PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl, const details::cxx20::type_identity_t<T> &value)

again line breaks

> + * is that the application is only notified once, after \a func has returned.
> + *
> + * \note Calling any overload of metadataAvailable() inside \a func is not allowed.
> + * \note The object passed to \a func is only usable while \a func runs, it must not
> + *       be saved or reused.
> + *
> + * \context This function shall be called from the CameraManager thread.
> + *
> + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> + */
> +
>  /**
>   * \brief Complete a buffer for a request
>   * \param[in] request The request the buffer belongs to
> --
> 2.50.1
>
Barnabás Pőcze July 28, 2025, 12:11 p.m. UTC | #2
Hi

2025. 07. 28. 12:08 keltezéssel, Jacopo Mondi írta:
> Hi Barnabás
> 
> On Mon, Jul 21, 2025 at 12:46:16PM +0200, Barnabás Pőcze wrote:
>> From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>>
>> Currently the way pipeline handlers have to store metadata results
>> for a Request is to access the Request::metadata_ list directly and
>> either merge a ControlList or set a single control value there.
>>
>> Direct access to Request::metadata_ is however problematic as even if
>> metadata would be available earlier, pipeline handlers can only
>> accumulate the results in Request::metadata_ and they're only available
>> for applications at Request complete time.
>>
>> Instead of letting pipeline handlers access Request::metadata_ directly
>> provide two helper functions, similar in spirit to
>> PipelineHandler::completeBuffer() and
>> PipelineHandler::completeRequest(), to allow pipeline handlers to
>> notify early availability of metadata.
>>
>> Provide three overloads, one that accepts a ControlList and merges it into
>> Request::metadata_, one that allows to set a single metadata result there
>> without going through an intermediate copy, and one that runs a callback
>> for conditional metadata reporting without intermediate an ControlList.
>>
>> The newly provided helpers trigger the Camera::availableMetadata signal
>> from where applications can retrieve the list of controls that have
>> just been made available by the pipeline handler.
>>
>> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
>> [Fill both lists, use `type_identity`, new overload, adjust commit message.]
>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>> ---
>> Original: https://patchwork.libcamera.org/patch/22229/
>>
>> changes in v2:
>>    * add new overload that takes an invocable object for more flexibility
>> ---
>>   include/libcamera/internal/pipeline_handler.h | 49 +++++++++++
>>   src/libcamera/pipeline_handler.cpp            | 84 +++++++++++++++++++
>>   2 files changed, 133 insertions(+)
>>
>> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
>> index e89d6a33e..49c907178 100644
>> --- a/include/libcamera/internal/pipeline_handler.h
>> +++ b/include/libcamera/internal/pipeline_handler.h
>> @@ -7,16 +7,21 @@
>>
>>   #pragma once
>>
>> +#include <functional>
>>   #include <memory>
>>   #include <string>
>>   #include <sys/types.h>
>>   #include <vector>
>>
>> +#include <libcamera/base/details/cxx20.h>
>>   #include <libcamera/base/object.h>
>>
>> +#include <libcamera/camera.h>
>>   #include <libcamera/controls.h>
>>   #include <libcamera/stream.h>
>>
>> +#include "libcamera/internal/request.h"
>> +
>>   namespace libcamera {
>>
>>   class Camera;
>> @@ -58,6 +63,50 @@ public:
>>   	void registerRequest(Request *request);
>>   	void queueRequest(Request *request);
>>
>> +	void metadataAvailable(Request *request, const ControlList &metadata);
>> +
>> +	template<typename T>
>> +	void metadataAvailable(Request *request, const Control<T> &ctrl,
>> +			       const details::cxx20::type_identity_t<T> &value)
> 
> I presume the usage of type_identity_t<T> is a safety measure to avoid
> a deduction failure. However I presume T is fully specified  and the
> type of the last argument should match the Control<T> one.
> 
> Howver I presume this doesn't hurt, and might save some deduction
> failure ?

I think it's more of a convenience feature, it allows you to use
e.g. initializer lists for the value, e.g.

    Rectangle *ptr; size_t count;
    metadataAvailable(controls::rpi::ScalerScrops, { ptr, count });

    Rectangle item1, item2, item3;
    metadataAvailable(controls::rpi::ScalerScrops, {{ item1, item2, item3  }});

It can be dropped if it seems unsuitable for some reason.


> 
>> +	{
>> +		auto &m = request->metadata2();
>> +		const auto c = m.checkpoint();
>> +
>> +		m.set(ctrl, value);
>> +		request->metadata().set(ctrl, value);
>> +
>> +		const auto d = c.diffSince();
>> +		if (d)
>> +			request->_d()->camera()->metadataAvailable.emit(request, d);
>> +	}
>> +
>> +#ifndef __DOXYGEN__
>> +	struct MetadataSetter {
>> +		Request *request;
>> +
>> +		template<typename T>
>> +		void operator()(const Control<T> &ctrl,
>> +			        const details::cxx20::type_identity_t<T> &value) const
>> +		{
>> +			request->metadata().set(ctrl, value);
>> +			request->metadata2().set(ctrl, value);
>> +		}
>> +	};
>> +
>> +	template<typename Func, std::enable_if_t<std::is_invocable_v<Func&, MetadataSetter>> * = nullptr>
>> +#else
>> +	template<typename Func>
>> +#endif
>> +	void metadataAvailable(Request *request, Func func)
>> +	{
>> +		const auto c = request->metadata2().checkpoint();
>> +
>> +		std::invoke(func, MetadataSetter{ request });
>> +
>> +		if (const auto d = c.diffSince())
>> +			request->_d()->camera()->metadataAvailable.emit(request, d);
>> +	}
>> +
>>   	bool completeBuffer(Request *request, FrameBuffer *buffer);
>>   	void completeRequest(Request *request);
>>   	void cancelRequest(Request *request);
>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
>> index e5f9e55c9..edfa9cf58 100644
>> --- a/src/libcamera/pipeline_handler.cpp
>> +++ b/src/libcamera/pipeline_handler.cpp
>> @@ -532,6 +532,90 @@ void PipelineHandler::doQueueRequests(Camera *camera)
>>    * \return 0 on success or a negative error code otherwise
>>    */
>>
>> +/**
>> + * \brief Signal the availability of metadata for \a request
>> + * \param[in] request The request the metadata belongs to
>> + * \param[in] metadata The collection of metadata items
>> + *
>> + * This function copies metadata items from \a metadata to the cumulative metadata
>> + * collection of \a request. This function may be called multiple times, but metadata
>> + * items already present in Request::metadata() are ignored. Afterwards the function
>> + * notifies the application by triggering the Camera::availableMetadata signal with
>> + * the just added metadata items.
>> + *
>> + * Early metadata completion allows pipeline handlers to fast track delivery of
>> + * metadata results as soon as they are available before the completion of \a
>> + * request. The full list of metadata results of a Request is available at
>> + * Request completion time in Request::metadata().
>> + *
>> + * \context This function shall be called from the CameraManager thread.
>> + *
>> + * \sa PipelineHandler::metadataAvailable(Request *request, Func func)
>> + * \sa PipelineHandler::metadataAvailable(Request *request,
>> + *                                        const Control<T> &ctrl,
>> + *                                        const details::cxx20::type_identity_t<T> &value)
>> + */
>> +void PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
>> +{
>> +	request->metadata().merge(metadata);
>> +
>> +	const auto d = request->metadata2().merge(metadata);
>> +	if (d)
>> +		request->_d()->camera()->metadataAvailable.emit(request, d);
>> +}
>> +
>> +/**
>> + * \fn void PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl,
>> + *					       const details::cxx20::type_identity_t<T> &value)
>> + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
>> + * \param[in] request The request the metadata belongs to
>> + * \param[in] ctrl The control id of the metadata item
>> + * \param[in] value The value of the metadata item
>> + *
>> + * This function servers the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> 
> you can break the line as
> 
>   * This function servers the same purpose as
>   * PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)

ok


> 
> 
>> + * but it allows a single metadata item to be reported directly,
>> + * without creating a ControlList.
>> + *
>> + * \context This function shall be called from the CameraManager thread.
>> + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
>> + */
>> +
>> +/**
>> + * \fn void PipelineHandler::metadataAvailable(Request *request, Func func)
>> + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
>> + * \param[in] request The request the metadata belongs to
>> + * \param[in] func The callback to invoke
>> + *
>> + * This function serves the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> 
> ditto

ok


> 
>> + * but it provides more flexibility for pipeline handlers. This function invokes
>> + * \a func, which receives as its sole argument an object of unspecified type whose
>> + * operator() can be used to add metadata items.
>> + *
>> + * For example, a PipelineHandler might use this function to conditionally report two
>> + * metadata items without creating an intermediate ControlList:
>> + *
>> + * \code
>> +	metadataAvailable(request, [&](auto set) {
>> +		if (x)
>> +			set(controls::X, x);
>> +		if (y)
>> +			set(controls::Y, y);
>> +		// ...
>> +	});
>> + * \endcode
> 
> Sorry I might be missing the use case here.
> 
> The call receives a MetadataSetter as 'set' (again auto requires a few
> levels of indirection just to save a few characters), but I don't get
> how the pipeline handler should call this, in particular what
> 
>          if (x)
> 
>          if (y)
> 
> might refer to...
> 
> What I mean is that pipeline handlers call metadataAvailable in
> response to events, and they should provide a callback where they
> inspect 'x' and 'y' which are conditions related to the event that
> just happened ?

I hope that the use case in one of the later patches makes things clearer.
I will try to make the example clearer in any case.


> 
>> + *
>> + * The advantage of the above over two calls to PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl, const details::cxx20::type_identity_t<T> &value)
> 
> again line breaks

ok


Regards,
Barnabás Pőcze

> 
>> + * is that the application is only notified once, after \a func has returned.
>> + *
>> + * \note Calling any overload of metadataAvailable() inside \a func is not allowed.
>> + * \note The object passed to \a func is only usable while \a func runs, it must not
>> + *       be saved or reused.
>> + *
>> + * \context This function shall be called from the CameraManager thread.
>> + *
>> + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
>> + */
>> +
>>   /**
>>    * \brief Complete a buffer for a request
>>    * \param[in] request The request the buffer belongs to
>> --
>> 2.50.1
>>
Jacopo Mondi July 28, 2025, 12:34 p.m. UTC | #3
Hi Barnabás

On Mon, Jul 28, 2025 at 02:11:27PM +0200, Barnabás Pőcze wrote:
> Hi
>
> 2025. 07. 28. 12:08 keltezéssel, Jacopo Mondi írta:
> > Hi Barnabás
> >
> > On Mon, Jul 21, 2025 at 12:46:16PM +0200, Barnabás Pőcze wrote:
> > > From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > >
> > > Currently the way pipeline handlers have to store metadata results
> > > for a Request is to access the Request::metadata_ list directly and
> > > either merge a ControlList or set a single control value there.
> > >
> > > Direct access to Request::metadata_ is however problematic as even if
> > > metadata would be available earlier, pipeline handlers can only
> > > accumulate the results in Request::metadata_ and they're only available
> > > for applications at Request complete time.
> > >
> > > Instead of letting pipeline handlers access Request::metadata_ directly
> > > provide two helper functions, similar in spirit to
> > > PipelineHandler::completeBuffer() and
> > > PipelineHandler::completeRequest(), to allow pipeline handlers to
> > > notify early availability of metadata.
> > >
> > > Provide three overloads, one that accepts a ControlList and merges it into
> > > Request::metadata_, one that allows to set a single metadata result there
> > > without going through an intermediate copy, and one that runs a callback
> > > for conditional metadata reporting without intermediate an ControlList.
> > >
> > > The newly provided helpers trigger the Camera::availableMetadata signal
> > > from where applications can retrieve the list of controls that have
> > > just been made available by the pipeline handler.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> > > [Fill both lists, use `type_identity`, new overload, adjust commit message.]
> > > Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> > > ---
> > > Original: https://patchwork.libcamera.org/patch/22229/
> > >
> > > changes in v2:
> > >    * add new overload that takes an invocable object for more flexibility
> > > ---
> > >   include/libcamera/internal/pipeline_handler.h | 49 +++++++++++
> > >   src/libcamera/pipeline_handler.cpp            | 84 +++++++++++++++++++
> > >   2 files changed, 133 insertions(+)
> > >
> > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > > index e89d6a33e..49c907178 100644
> > > --- a/include/libcamera/internal/pipeline_handler.h
> > > +++ b/include/libcamera/internal/pipeline_handler.h
> > > @@ -7,16 +7,21 @@
> > >
> > >   #pragma once
> > >
> > > +#include <functional>
> > >   #include <memory>
> > >   #include <string>
> > >   #include <sys/types.h>
> > >   #include <vector>
> > >
> > > +#include <libcamera/base/details/cxx20.h>
> > >   #include <libcamera/base/object.h>
> > >
> > > +#include <libcamera/camera.h>
> > >   #include <libcamera/controls.h>
> > >   #include <libcamera/stream.h>
> > >
> > > +#include "libcamera/internal/request.h"
> > > +
> > >   namespace libcamera {
> > >
> > >   class Camera;
> > > @@ -58,6 +63,50 @@ public:
> > >   	void registerRequest(Request *request);
> > >   	void queueRequest(Request *request);
> > >
> > > +	void metadataAvailable(Request *request, const ControlList &metadata);
> > > +
> > > +	template<typename T>
> > > +	void metadataAvailable(Request *request, const Control<T> &ctrl,
> > > +			       const details::cxx20::type_identity_t<T> &value)
> >
> > I presume the usage of type_identity_t<T> is a safety measure to avoid
> > a deduction failure. However I presume T is fully specified  and the
> > type of the last argument should match the Control<T> one.
> >
> > Howver I presume this doesn't hurt, and might save some deduction
> > failure ?
>
> I think it's more of a convenience feature, it allows you to use
> e.g. initializer lists for the value, e.g.
>
>    Rectangle *ptr; size_t count;
>    metadataAvailable(controls::rpi::ScalerScrops, { ptr, count });
>
>    Rectangle item1, item2, item3;
>    metadataAvailable(controls::rpi::ScalerScrops, {{ item1, item2, item3  }});
>
> It can be dropped if it seems unsuitable for some reason.
>

No no, I think initializer_lists are a a valid case of non-deduced
context where type_identity_t might help.

As said it's certainly doesn't hurt, even more if it allows usage of
useful features

>
> >
> > > +	{
> > > +		auto &m = request->metadata2();
> > > +		const auto c = m.checkpoint();
> > > +
> > > +		m.set(ctrl, value);
> > > +		request->metadata().set(ctrl, value);
> > > +
> > > +		const auto d = c.diffSince();
> > > +		if (d)
> > > +			request->_d()->camera()->metadataAvailable.emit(request, d);
> > > +	}
> > > +
> > > +#ifndef __DOXYGEN__
> > > +	struct MetadataSetter {
> > > +		Request *request;
> > > +
> > > +		template<typename T>
> > > +		void operator()(const Control<T> &ctrl,
> > > +			        const details::cxx20::type_identity_t<T> &value) const
> > > +		{
> > > +			request->metadata().set(ctrl, value);
> > > +			request->metadata2().set(ctrl, value);
> > > +		}
> > > +	};
> > > +
> > > +	template<typename Func, std::enable_if_t<std::is_invocable_v<Func&, MetadataSetter>> * = nullptr>
> > > +#else
> > > +	template<typename Func>
> > > +#endif
> > > +	void metadataAvailable(Request *request, Func func)
> > > +	{
> > > +		const auto c = request->metadata2().checkpoint();
> > > +
> > > +		std::invoke(func, MetadataSetter{ request });
> > > +
> > > +		if (const auto d = c.diffSince())
> > > +			request->_d()->camera()->metadataAvailable.emit(request, d);
> > > +	}
> > > +
> > >   	bool completeBuffer(Request *request, FrameBuffer *buffer);
> > >   	void completeRequest(Request *request);
> > >   	void cancelRequest(Request *request);
> > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > index e5f9e55c9..edfa9cf58 100644
> > > --- a/src/libcamera/pipeline_handler.cpp
> > > +++ b/src/libcamera/pipeline_handler.cpp
> > > @@ -532,6 +532,90 @@ void PipelineHandler::doQueueRequests(Camera *camera)
> > >    * \return 0 on success or a negative error code otherwise
> > >    */
> > >
> > > +/**
> > > + * \brief Signal the availability of metadata for \a request
> > > + * \param[in] request The request the metadata belongs to
> > > + * \param[in] metadata The collection of metadata items
> > > + *
> > > + * This function copies metadata items from \a metadata to the cumulative metadata
> > > + * collection of \a request. This function may be called multiple times, but metadata
> > > + * items already present in Request::metadata() are ignored. Afterwards the function
> > > + * notifies the application by triggering the Camera::availableMetadata signal with
> > > + * the just added metadata items.
> > > + *
> > > + * Early metadata completion allows pipeline handlers to fast track delivery of
> > > + * metadata results as soon as they are available before the completion of \a
> > > + * request. The full list of metadata results of a Request is available at
> > > + * Request completion time in Request::metadata().
> > > + *
> > > + * \context This function shall be called from the CameraManager thread.
> > > + *
> > > + * \sa PipelineHandler::metadataAvailable(Request *request, Func func)
> > > + * \sa PipelineHandler::metadataAvailable(Request *request,
> > > + *                                        const Control<T> &ctrl,
> > > + *                                        const details::cxx20::type_identity_t<T> &value)
> > > + */
> > > +void PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> > > +{
> > > +	request->metadata().merge(metadata);
> > > +
> > > +	const auto d = request->metadata2().merge(metadata);
> > > +	if (d)
> > > +		request->_d()->camera()->metadataAvailable.emit(request, d);
> > > +}
> > > +
> > > +/**
> > > + * \fn void PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl,
> > > + *					       const details::cxx20::type_identity_t<T> &value)
> > > + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> > > + * \param[in] request The request the metadata belongs to
> > > + * \param[in] ctrl The control id of the metadata item
> > > + * \param[in] value The value of the metadata item
> > > + *
> > > + * This function servers the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> >
> > you can break the line as
> >
> >   * This function servers the same purpose as
> >   * PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
>
> ok
>
>
> >
> >
> > > + * but it allows a single metadata item to be reported directly,
> > > + * without creating a ControlList.
> > > + *
> > > + * \context This function shall be called from the CameraManager thread.
> > > + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> > > + */
> > > +
> > > +/**
> > > + * \fn void PipelineHandler::metadataAvailable(Request *request, Func func)
> > > + * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> > > + * \param[in] request The request the metadata belongs to
> > > + * \param[in] func The callback to invoke
> > > + *
> > > + * This function serves the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> >
> > ditto
>
> ok
>
>
> >
> > > + * but it provides more flexibility for pipeline handlers. This function invokes
> > > + * \a func, which receives as its sole argument an object of unspecified type whose
> > > + * operator() can be used to add metadata items.
> > > + *
> > > + * For example, a PipelineHandler might use this function to conditionally report two
> > > + * metadata items without creating an intermediate ControlList:
> > > + *
> > > + * \code
> > > +	metadataAvailable(request, [&](auto set) {
> > > +		if (x)
> > > +			set(controls::X, x);
> > > +		if (y)
> > > +			set(controls::Y, y);
> > > +		// ...
> > > +	});
> > > + * \endcode
> >
> > Sorry I might be missing the use case here.
> >
> > The call receives a MetadataSetter as 'set' (again auto requires a few
> > levels of indirection just to save a few characters), but I don't get
> > how the pipeline handler should call this, in particular what
> >
> >          if (x)
> >
> >          if (y)
> >
> > might refer to...
> >
> > What I mean is that pipeline handlers call metadataAvailable in
> > response to events, and they should provide a callback where they
> > inspect 'x' and 'y' which are conditions related to the event that
> > just happened ?
>
> I hope that the use case in one of the later patches makes things clearer.

Yes sure, let's continue the discussion there

Thanks
  j

> I will try to make the example clearer in any case.
>
>
> >
> > > + *
> > > + * The advantage of the above over two calls to PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl, const details::cxx20::type_identity_t<T> &value)
> >
> > again line breaks
>
> ok
>
>
> Regards,
> Barnabás Pőcze
>
> >
> > > + * is that the application is only notified once, after \a func has returned.
> > > + *
> > > + * \note Calling any overload of metadataAvailable() inside \a func is not allowed.
> > > + * \note The object passed to \a func is only usable while \a func runs, it must not
> > > + *       be saved or reused.
> > > + *
> > > + * \context This function shall be called from the CameraManager thread.
> > > + *
> > > + * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
> > > + */
> > > +
> > >   /**
> > >    * \brief Complete a buffer for a request
> > >    * \param[in] request The request the buffer belongs to
> > > --
> > > 2.50.1
> > >
>

Patch
diff mbox series

diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index e89d6a33e..49c907178 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -7,16 +7,21 @@ 
 
 #pragma once
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <sys/types.h>
 #include <vector>
 
+#include <libcamera/base/details/cxx20.h>
 #include <libcamera/base/object.h>
 
+#include <libcamera/camera.h>
 #include <libcamera/controls.h>
 #include <libcamera/stream.h>
 
+#include "libcamera/internal/request.h"
+
 namespace libcamera {
 
 class Camera;
@@ -58,6 +63,50 @@  public:
 	void registerRequest(Request *request);
 	void queueRequest(Request *request);
 
+	void metadataAvailable(Request *request, const ControlList &metadata);
+
+	template<typename T>
+	void metadataAvailable(Request *request, const Control<T> &ctrl,
+			       const details::cxx20::type_identity_t<T> &value)
+	{
+		auto &m = request->metadata2();
+		const auto c = m.checkpoint();
+
+		m.set(ctrl, value);
+		request->metadata().set(ctrl, value);
+
+		const auto d = c.diffSince();
+		if (d)
+			request->_d()->camera()->metadataAvailable.emit(request, d);
+	}
+
+#ifndef __DOXYGEN__
+	struct MetadataSetter {
+		Request *request;
+
+		template<typename T>
+		void operator()(const Control<T> &ctrl,
+			        const details::cxx20::type_identity_t<T> &value) const
+		{
+			request->metadata().set(ctrl, value);
+			request->metadata2().set(ctrl, value);
+		}
+	};
+
+	template<typename Func, std::enable_if_t<std::is_invocable_v<Func&, MetadataSetter>> * = nullptr>
+#else
+	template<typename Func>
+#endif
+	void metadataAvailable(Request *request, Func func)
+	{
+		const auto c = request->metadata2().checkpoint();
+
+		std::invoke(func, MetadataSetter{ request });
+
+		if (const auto d = c.diffSince())
+			request->_d()->camera()->metadataAvailable.emit(request, d);
+	}
+
 	bool completeBuffer(Request *request, FrameBuffer *buffer);
 	void completeRequest(Request *request);
 	void cancelRequest(Request *request);
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index e5f9e55c9..edfa9cf58 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -532,6 +532,90 @@  void PipelineHandler::doQueueRequests(Camera *camera)
  * \return 0 on success or a negative error code otherwise
  */
 
+/**
+ * \brief Signal the availability of metadata for \a request
+ * \param[in] request The request the metadata belongs to
+ * \param[in] metadata The collection of metadata items
+ *
+ * This function copies metadata items from \a metadata to the cumulative metadata
+ * collection of \a request. This function may be called multiple times, but metadata
+ * items already present in Request::metadata() are ignored. Afterwards the function
+ * notifies the application by triggering the Camera::availableMetadata signal with
+ * the just added metadata items.
+ *
+ * Early metadata completion allows pipeline handlers to fast track delivery of
+ * metadata results as soon as they are available before the completion of \a
+ * request. The full list of metadata results of a Request is available at
+ * Request completion time in Request::metadata().
+ *
+ * \context This function shall be called from the CameraManager thread.
+ *
+ * \sa PipelineHandler::metadataAvailable(Request *request, Func func)
+ * \sa PipelineHandler::metadataAvailable(Request *request,
+ *                                        const Control<T> &ctrl,
+ *                                        const details::cxx20::type_identity_t<T> &value)
+ */
+void PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+{
+	request->metadata().merge(metadata);
+
+	const auto d = request->metadata2().merge(metadata);
+	if (d)
+		request->_d()->camera()->metadataAvailable.emit(request, d);
+}
+
+/**
+ * \fn void PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl,
+ *					       const details::cxx20::type_identity_t<T> &value)
+ * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+ * \param[in] request The request the metadata belongs to
+ * \param[in] ctrl The control id of the metadata item
+ * \param[in] value The value of the metadata item
+ *
+ * This function servers the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+ * but it allows a single metadata item to be reported directly,
+ * without creating a ControlList.
+ *
+ * \context This function shall be called from the CameraManager thread.
+ * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+ */
+
+/**
+ * \fn void PipelineHandler::metadataAvailable(Request *request, Func func)
+ * \copybrief PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+ * \param[in] request The request the metadata belongs to
+ * \param[in] func The callback to invoke
+ *
+ * This function serves the same purpose as PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+ * but it provides more flexibility for pipeline handlers. This function invokes
+ * \a func, which receives as its sole argument an object of unspecified type whose
+ * operator() can be used to add metadata items.
+ *
+ * For example, a PipelineHandler might use this function to conditionally report two
+ * metadata items without creating an intermediate ControlList:
+ *
+ * \code
+	metadataAvailable(request, [&](auto set) {
+		if (x)
+			set(controls::X, x);
+		if (y)
+			set(controls::Y, y);
+		// ...
+	});
+ * \endcode
+ *
+ * The advantage of the above over two calls to PipelineHandler::metadataAvailable(Request *request, const Control<T> &ctrl, const details::cxx20::type_identity_t<T> &value)
+ * is that the application is only notified once, after \a func has returned.
+ *
+ * \note Calling any overload of metadataAvailable() inside \a func is not allowed.
+ * \note The object passed to \a func is only usable while \a func runs, it must not
+ *       be saved or reused.
+ *
+ * \context This function shall be called from the CameraManager thread.
+ *
+ * \sa PipelineHandler::metadataAvailable(Request *request, const ControlList &metadata)
+ */
+
 /**
  * \brief Complete a buffer for a request
  * \param[in] request The request the buffer belongs to