[PATCH 4/4] gstreamer: Split metadata controls into new member

Jaslo Ziska jaslo at ziska.de
Wed Apr 23 12:00:09 CEST 2025


Hi Nicolas, Kieran, Laurent,

Laurent Pinchart <laurent.pinchart at ideasonboard.com> writes:
> On Tue, Apr 22, 2025 at 04:33:02PM +0100, Kieran Bingham wrote:
>> Quoting Nicolas Dufresne (2025-04-22 15:42:41)
>> > Le mardi 22 avril 2025 à 16:11 +0200, Jaslo Ziska a écrit :
>> > > When the libcamerasrc element is stopped and started again, 
>> > > the
>> > > GstCameraControls::setCamera() function is called, but now 
>> > > it is
>> > > populated with metadata returned by the previous requests. 
>> > > Because
>> > > GstCameraControls::setCamera() filters the controls, this 
>> > > would cause
>> > > warnings to be emitted, as the returned metadata are not 
>> > > valid controls
>> > > which can be set.
>> >
>> > Does it also fixes on top the issue where controls that get 
>> > applied
>> > with delays get resets and forgotten ?

Sorry, I don't know which issue you are referring to.

>> >
>> > > This is fixed by introducing a new member which only holds 
>> > > the metadata
>> > > returned by the requests, so that the controls and the 
>> > > metadata are now
>> > > completely independent from each other.
>> > >
>> > > Signed-off-by: Jaslo Ziska <jaslo at ziska.de>
>> > > ---
>> > >  src/gstreamer/gstlibcamera-controls.cpp.in | 22 
>> > > ++++++++++++++++------
>> > >  src/gstreamer/gstlibcamera-controls.h      |  4 +++-
>> > >  2 files changed, 19 insertions(+), 7 deletions(-)
>> > >
>> > > diff --git a/src/gstreamer/gstlibcamera-controls.cpp.in 
>> > > b/src/gstreamer/gstlibcamera-controls.cpp.in
>> > > index 89c530da..490e835e 100644
>> > > --- a/src/gstreamer/gstlibcamera-controls.cpp.in
>> > > +++ b/src/gstreamer/gstlibcamera-controls.cpp.in
>> > > @@ -153,20 +153,24 @@ g_param_spec_{{ ctrl.gtype }}(
>> > >  bool GstCameraControls::getProperty(guint propId, GValue 
>> > > *value,
>> > >                                   [[maybe_unused]] 
>> > > GParamSpec *pspec)
>> > >  {
>> > > -     if (!controls_acc_.contains(propId)) {
>> > > +     const ControlValue *cv;
>> > > +     if (controls_acc_.contains(propId)) {
>> > > +             cv = &controls_acc_.get(propId);
>> > > +     } else if (metadata_.contains(propId)) {
>> > > +             cv = &metadata_.get(propId);
>
> Why do controls take precedence here ?

I thought it would make sense, but I was wrong, see below.

>
> Taking a step back, does GSreamer expect the value of properties 
> to be
> changed dynamically by the device ? If, for instance, 
> auto-exposure is
> enabled, does GStreamer expect/allow the exposure time and gain
> properties to be modified to reflect the AGC calculation ?

In theory these modified values should be available as properties 
if they are in the metadata returned by the requests. But from the 
rest of your comments I see that there is a mistake in my logic, 
see below.

>
>> > > +     } else {
>> > >               GST_WARNING("Control '%s' is not available, 
>> > > default value will "
>> > >                           "be returned",
>> > >                           
>> > > controls::controls.at(propId)->name().c_str());
>> > >               return true;
>> > >       }
>> > > -     const ControlValue &cv = controls_acc_.get(propId);
>> > >
>> > >       switch (propId) {
>> > >  {%- for vendor, ctrls in controls %}
>> > >  {%- for ctrl in ctrls %}
>> > >
>> > >       case controls::{{ ctrl.namespace }}{{ 
>> > > ctrl.name|snake_case|upper }}: {
>> > > -             auto control = cv.get<{{ ctrl.type }}>();
>> > > +             auto control = cv->get<{{ ctrl.type }}>();
>> > >
>> > >  {%- if ctrl.is_array %}
>> > >               for (size_t i = 0; i < control.size(); ++i) {
>> > > @@ -309,9 +313,14 @@ void 
>> > > GstCameraControls::setCamera(const 
>> > > std::shared_ptr<libcamera::Camera> &cam)
>> > >                                   "camera and will be 
>> > > ignored",
>> > >                                   cid->name().c_str());
>> > >       }
>> > > -
>
> Please keep the blank line here.
>
>> > >       controls_acc_ = new_controls;
>> > >       controls_ = new_controls;
>> > > +
>> > > +     /*
>> > > +      * Clear metadata as this might already be populated 
>> > > if the element has
>> > > +      * been running before.
>> > > +      */
>> > > +     metadata_.clear();
>> > >  }
>> > >
>> > >  void 
>> > > GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> 
>> > > &request)
>> > > @@ -322,6 +331,7 @@ void 
>> > > GstCameraControls::applyControls(std::unique_ptr<libcamera::Request> 
>> > > &reque
>> > >
>> > >  void GstCameraControls::readMetadata(libcamera::Request 
>> > > *request)
>> > >  {
>> > > -     controls_acc_.merge(request->metadata(),
>> > > -                         
>> > > ControlList::MergePolicy::OverwriteExisting);
>> > > +     metadata_.merge(request->metadata(),
>> > > + 
>> > > ControlList::MergePolicy::OverwriteExisting);
>> > > +
>> > >  }
>> > > diff --git a/src/gstreamer/gstlibcamera-controls.h 
>> > > b/src/gstreamer/gstlibcamera-controls.h
>> > > index 749220b5..ee97e61e 100644
>> > > --- a/src/gstreamer/gstlibcamera-controls.h
>> > > +++ b/src/gstreamer/gstlibcamera-controls.h
>> > > @@ -36,8 +36,10 @@ private:
>> > >       ControlInfoMap capabilities_;
>> > >       /* Set of user modified controls. */
>> > >       ControlList controls_;
>> > > -     /* Accumulator of all controls ever set and metadata 
>> > > returned by camera */
>> > > +     /* Accumulator of all controls ever */
>
> s/ever/ever set/
>
>> > >       ControlList controls_acc_;
>>
>> Do we need a controls_acc_ at all ? What does this do ?
>>
>> I'm weary because some of the controls are 'triggers' so they 
>> should
>> only be set once - not on every frame ... ?

Now I'm confused, I thought all controls should only be set once 
when they changed? controls_acc_ was introduced for this. The 
controls_ member holds the controls which will be set on the next 
request and then gets cleared (so that they only get set once). 
But the control_acc_ variable accumulates the controls so that 
they can be returned to GSreamer in getProperty().

>
> There are also dependencies between some controls, and I don't 
> think we
> guarantee that setting A=a, B=b in the same request will have 
> the same
> effect as setting A=a in a first request and B=b in a second 
> request.
> Accumulating the controls may not match the actual state of the 
> device.
>
> For instance, look at the documentation of ColourGains and
> ColourTemperature. They respectively state
>
>         If ColourGains is set in a request but ColourTemperature 
>         is not, the
>         implementation shall calculate and set the 
>         ColourTemperature based on
>         the ColourGains.
>
> and
>
>         If ColourTemperature is set in a request but ColourGains 
>         is not, the
>         implementation shall calculate and set the ColourGains 
>         based on the
>         given ColourTemperature.
>
> Setting ColourGains=cg in a first request and and 
> ColourTemperature=ct
> in a second request means that ColourGains will end up being 
> calculated
> as CG(ct). Setting them both in the same request means they will 
> both
> have the values specified by the user. By accumulating controls, 
> we lose
> this information.

Thanks for the explanation. Then it seems like it was correct the 
way it was before? Because when ColourGains is set in the first 
request controls_acc_ holds that value and if at a later point 
ColourTemperature is set, ColourGains gets overwritten by the 
value returned by the metadata of the request. Or am I mistaken?

Best Regards
Jaslo

>
>> > > +     /* Metadata returned by requests */
>> > > +     ControlList metadata_;
>> > >  };
>> > >
>> > >  } /* namespace libcamera */
>> >
>> > Looks good to me, but I'd like someone from the core to 
>> > review this
>> > please.


More information about the libcamera-devel mailing list