[v1,7/7] pipeline: rpi: Handler controls::rpi::ScalerCrops
diff mbox series

Message ID 20240808102346.13065-8-naush@raspberrypi.com
State Superseded, archived
Headers show
Series
  • Raspberry Pi: Add controls::rpi::ScalerCrops
Related show

Commit Message

Naushir Patuck Aug. 8, 2024, 10:23 a.m. UTC
Handle multiple scaler crops being set through the rpi::ScalerCrops
control. We now populate the cropParams_ map in the loop where we handle
the output stream configuration items. The key of this map is the index
of the stream configuration structure set by the application. This will
also be the same index used to specify the crop rectangles through the
ScalerCrops control.

CameraData::applyScalerCrop() has been adapted to look at either
controls::ScalerCrop or controls::rpi::ScalerCrops. The former takes
priority over the latter, and if present, will apply the same scaler
crop to all output streams.

Finally return all crops through the same ScalerCrops control via
request metadata. The first configure stream's crop rectangle is also
returned via the ScalerCrop control in the request metadata.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
---
 .../pipeline/rpi/common/pipeline_base.cpp     | 69 +++++++++++++++----
 1 file changed, 56 insertions(+), 13 deletions(-)

Comments

Jacopo Mondi Aug. 28, 2024, 10:07 a.m. UTC | #1
Hi Naush

On Thu, Aug 08, 2024 at 11:23:46AM GMT, Naushir Patuck wrote:
> Handle multiple scaler crops being set through the rpi::ScalerCrops
> control. We now populate the cropParams_ map in the loop where we handle
> the output stream configuration items. The key of this map is the index
> of the stream configuration structure set by the application. This will
> also be the same index used to specify the crop rectangles through the
> ScalerCrops control.
>
> CameraData::applyScalerCrop() has been adapted to look at either
> controls::ScalerCrop or controls::rpi::ScalerCrops. The former takes
> priority over the latter, and if present, will apply the same scaler
> crop to all output streams.
>
> Finally return all crops through the same ScalerCrops control via
> request metadata. The first configure stream's crop rectangle is also
> returned via the ScalerCrop control in the request metadata.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> ---
>  .../pipeline/rpi/common/pipeline_base.cpp     | 69 +++++++++++++++----
>  1 file changed, 56 insertions(+), 13 deletions(-)
>
> diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> index a6ea4e9c47dd..b9759b682f0d 100644
> --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> @@ -561,11 +561,28 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
>  	for (auto const &c : result.controlInfo)
>  		ctrlMap.emplace(c.first, c.second);
>
> -	if (data->cropParams_.count(0)) {
> -		/* Add the ScalerCrop control limits based on the current mode. */
> +	if (data->cropParams_.size()) {

This comment applies to the previous patch, but is there a case where
data->cropParams_ is not populated at all ?

> +		/*
> +		 * Add the ScalerCrop control limits based on the current mode and
> +		 * the first configured stream.
> +		 */
>  		Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(data->cropParams_[0].ispMinCropSize));
>  		ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,
>  							     data->scaleIspCrop(data->cropParams_[0].ispCrop));

empty line maybe ?

> +		if (data->cropParams_.size() == 2) {
> +			/*
> +			 * The control map for rpi::ScalerCrops has the min value
> +			 * as the default crop for stream 0, max value as the default
> +			 * value for stream 1.

mmm, I guess this is some internal agreement between pipeline_base and
pipeline_pisp that initializes cropParams in this way, right ?


> +			 */
> +			ctrlMap[&controls::rpi::ScalerCrops] =
> +				ControlInfo(data->scaleIspCrop(data->cropParams_[0].ispCrop),
> +					    data->scaleIspCrop(data->cropParams_[1].ispCrop),
> +					    ctrlMap[&controls::ScalerCrop].def());
> +		} else {
> +			/* Match the same ControlInfo for rpi::ScalerCrops. */
> +			ctrlMap[&controls::rpi::ScalerCrops] = ctrlMap[&controls::ScalerCrop];
> +		}
>  	}
>
>  	data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
> @@ -1292,10 +1309,29 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const
>
>  void CameraData::applyScalerCrop(const ControlList &controls)
>  {
> -	const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);
> -	if (scalerCrop && cropParams_.count(0)) {
> -		CropParams &cropParams = cropParams_[0];
> -		Rectangle nativeCrop = *scalerCrop;
> +	const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);
> +	const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);

can both be specified in the same requests ? Does the usage of
ScalerCrops make ScalerCrop invalid ?

It feels to me like pips should only register ScalerCrops and vc4 only
ScalerCrop. Yes, the application has to adapt to this in this case,
not sure how bad that would be

> +	std::vector<Rectangle> scalerCrops;
> +
> +	/*
> +	 * First thing to do is create a vector of crops to apply to each ISP output
> +	 * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if
> +	 * present.
> +	 *
> +	 * If controls::ScalerCrop is present, apply the same crop to all ISP output
> +	 * streams. Otherwise if controls::rpi::ScalerCrops, apply the given crops
> +	 * to the ISP output streams, indexed by the same order in which they had
> +	 * been configured. This is not the same as the ISP output index.
> +	 */
> +	for (unsigned int i = 0; i < cropParams_.size(); i++) {
> +		if (scalerCropRPi && i < scalerCropRPi->size())
> +			scalerCrops.push_back(scalerCropRPi->data()[i]);
> +		else if (scalerCropCore)
> +			scalerCrops.push_back(*scalerCropCore);
> +	}
> +
> +	for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {
> +		Rectangle nativeCrop = scalerCrop;
>
>  		if (!nativeCrop.width || !nativeCrop.height)
>  			nativeCrop = { 0, 0, 1, 1 };
> @@ -1311,13 +1347,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)
>  		 * 2. With the same mid-point, if possible.
>  		 * 3. But it can't go outside the sensor area.
>  		 */
> -		Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
> +		Size minSize = cropParams_[i].ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
>  		Size size = ispCrop.size().expandedTo(minSize);
>  		ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));
>
> -		if (ispCrop != cropParams.ispCrop) {
> -			cropParams.ispCrop = ispCrop;
> -			platformSetIspCrop(cropParams.ispIndex, ispCrop);
> +		if (ispCrop != cropParams_[i].ispCrop) {
> +			cropParams_[i].ispCrop = ispCrop;
> +			platformSetIspCrop(cropParams_[i].ispIndex, ispCrop);

You can really just pass cropsParams_[i] to platformSetIspCrop. It
contains the index and the updated ispCrop.

>  		}
>  	}
>  }
> @@ -1474,9 +1510,16 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request
>  	request->metadata().set(controls::SensorTimestamp,
>  				bufferControls.get(controls::SensorTimestamp).value_or(0));
>
> -	if (cropParams_.count(0))
> -		request->metadata().set(controls::ScalerCrop,
> -					scaleIspCrop(cropParams_[0].ispCrop));
> +	if (cropParams_.size()) {

Again I might have missed in which case cropParams_ is not populated
at all

> +		std::vector<Rectangle> crops;
> +
> +		for (auto const &[k, v] : cropParams_)
> +			crops.push_back(scaleIspCrop(v.ispCrop));
> +
> +		request->metadata().set(controls::ScalerCrop, crops[0]);
> +		request->metadata().set(controls::rpi::ScalerCrops,
> +					Span<const Rectangle>(crops.data(), crops.size()));
> +	}
>  }
>
>  } /* namespace libcamera */
> --
> 2.34.1
>
Naushir Patuck Aug. 28, 2024, 11:10 a.m. UTC | #2
Hi Jacopo,

Thanks for the review.

On Wed, 28 Aug 2024 at 11:07, Jacopo Mondi
<jacopo.mondi@ideasonboard.com> wrote:
>
> Hi Naush
>
> On Thu, Aug 08, 2024 at 11:23:46AM GMT, Naushir Patuck wrote:
> > Handle multiple scaler crops being set through the rpi::ScalerCrops
> > control. We now populate the cropParams_ map in the loop where we handle
> > the output stream configuration items. The key of this map is the index
> > of the stream configuration structure set by the application. This will
> > also be the same index used to specify the crop rectangles through the
> > ScalerCrops control.
> >
> > CameraData::applyScalerCrop() has been adapted to look at either
> > controls::ScalerCrop or controls::rpi::ScalerCrops. The former takes
> > priority over the latter, and if present, will apply the same scaler
> > crop to all output streams.
> >
> > Finally return all crops through the same ScalerCrops control via
> > request metadata. The first configure stream's crop rectangle is also
> > returned via the ScalerCrop control in the request metadata.
> >
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > ---
> >  .../pipeline/rpi/common/pipeline_base.cpp     | 69 +++++++++++++++----
> >  1 file changed, 56 insertions(+), 13 deletions(-)
> >
> > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> > index a6ea4e9c47dd..b9759b682f0d 100644
> > --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> > +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> > @@ -561,11 +561,28 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
> >       for (auto const &c : result.controlInfo)
> >               ctrlMap.emplace(c.first, c.second);
> >
> > -     if (data->cropParams_.count(0)) {
> > -             /* Add the ScalerCrop control limits based on the current mode. */
> > +     if (data->cropParams_.size()) {
>
> This comment applies to the previous patch, but is there a case where
> data->cropParams_ is not populated at all ?

Both vc4 and pisp will populate this.  I was being a bit too cautious
here perhaps.  I'll remove the check.

>
> > +             /*
> > +              * Add the ScalerCrop control limits based on the current mode and
> > +              * the first configured stream.
> > +              */
> >               Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(data->cropParams_[0].ispMinCropSize));
> >               ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,
> >                                                            data->scaleIspCrop(data->cropParams_[0].ispCrop));
>
> empty line maybe ?

ack

>
> > +             if (data->cropParams_.size() == 2) {
> > +                     /*
> > +                      * The control map for rpi::ScalerCrops has the min value
> > +                      * as the default crop for stream 0, max value as the default
> > +                      * value for stream 1.
>
> mmm, I guess this is some internal agreement between pipeline_base and
> pipeline_pisp that initializes cropParams in this way, right ?
>
>
> > +                      */
> > +                     ctrlMap[&controls::rpi::ScalerCrops] =
> > +                             ControlInfo(data->scaleIspCrop(data->cropParams_[0].ispCrop),
> > +                                         data->scaleIspCrop(data->cropParams_[1].ispCrop),
> > +                                         ctrlMap[&controls::ScalerCrop].def());
> > +             } else {
> > +                     /* Match the same ControlInfo for rpi::ScalerCrops. */
> > +                     ctrlMap[&controls::rpi::ScalerCrops] = ctrlMap[&controls::ScalerCrop];
> > +             }
> >       }
> >
> >       data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
> > @@ -1292,10 +1309,29 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const
> >
> >  void CameraData::applyScalerCrop(const ControlList &controls)
> >  {
> > -     const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);
> > -     if (scalerCrop && cropParams_.count(0)) {
> > -             CropParams &cropParams = cropParams_[0];
> > -             Rectangle nativeCrop = *scalerCrop;
> > +     const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);
> > +     const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);
>
> can both be specified in the same requests ? Does the usage of
> ScalerCrops make ScalerCrop invalid ?

If they are both specified in the same request,
controls::rpi::ScalerCrops will be used instead of
controls::ScalerCrop.

>
> It feels to me like pips should only register ScalerCrops and vc4 only
> ScalerCrop. Yes, the application has to adapt to this in this case,
> not sure how bad that would be

I think that would be workable.

>
> > +     std::vector<Rectangle> scalerCrops;
> > +
> > +     /*
> > +      * First thing to do is create a vector of crops to apply to each ISP output
> > +      * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if
> > +      * present.
> > +      *
> > +      * If controls::ScalerCrop is present, apply the same crop to all ISP output
> > +      * streams. Otherwise if controls::rpi::ScalerCrops, apply the given crops
> > +      * to the ISP output streams, indexed by the same order in which they had
> > +      * been configured. This is not the same as the ISP output index.
> > +      */
> > +     for (unsigned int i = 0; i < cropParams_.size(); i++) {
> > +             if (scalerCropRPi && i < scalerCropRPi->size())
> > +                     scalerCrops.push_back(scalerCropRPi->data()[i]);
> > +             else if (scalerCropCore)
> > +                     scalerCrops.push_back(*scalerCropCore);
> > +     }
> > +
> > +     for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {
> > +             Rectangle nativeCrop = scalerCrop;
> >
> >               if (!nativeCrop.width || !nativeCrop.height)
> >                       nativeCrop = { 0, 0, 1, 1 };
> > @@ -1311,13 +1347,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)
> >                * 2. With the same mid-point, if possible.
> >                * 3. But it can't go outside the sensor area.
> >                */
> > -             Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
> > +             Size minSize = cropParams_[i].ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
> >               Size size = ispCrop.size().expandedTo(minSize);
> >               ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));
> >
> > -             if (ispCrop != cropParams.ispCrop) {
> > -                     cropParams.ispCrop = ispCrop;
> > -                     platformSetIspCrop(cropParams.ispIndex, ispCrop);
> > +             if (ispCrop != cropParams_[i].ispCrop) {
> > +                     cropParams_[i].ispCrop = ispCrop;
> > +                     platformSetIspCrop(cropParams_[i].ispIndex, ispCrop);
>
> You can really just pass cropsParams_[i] to platformSetIspCrop. It
> contains the index and the updated ispCrop.

Ack.

>
> >               }
> >       }
> >  }
> > @@ -1474,9 +1510,16 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request
> >       request->metadata().set(controls::SensorTimestamp,
> >                               bufferControls.get(controls::SensorTimestamp).value_or(0));
> >
> > -     if (cropParams_.count(0))
> > -             request->metadata().set(controls::ScalerCrop,
> > -                                     scaleIspCrop(cropParams_[0].ispCrop));
> > +     if (cropParams_.size()) {
>
> Again I might have missed in which case cropParams_ is not populated
> at all

As above, I can remove the check.

Regards,
Naush

>
> > +             std::vector<Rectangle> crops;
> > +
> > +             for (auto const &[k, v] : cropParams_)
> > +                     crops.push_back(scaleIspCrop(v.ispCrop));
> > +
> > +             request->metadata().set(controls::ScalerCrop, crops[0]);
> > +             request->metadata().set(controls::rpi::ScalerCrops,
> > +                                     Span<const Rectangle>(crops.data(), crops.size()));
> > +     }
> >  }
> >
> >  } /* namespace libcamera */
> > --
> > 2.34.1
> >
Laurent Pinchart Aug. 28, 2024, 5:01 p.m. UTC | #3
Hello,

On Wed, Aug 28, 2024 at 12:10:18PM +0100, Naushir Patuck wrote:
> On Wed, 28 Aug 2024 at 11:07, Jacopo Mondi wrote:
> > On Thu, Aug 08, 2024 at 11:23:46AM GMT, Naushir Patuck wrote:
> > > Handle multiple scaler crops being set through the rpi::ScalerCrops
> > > control. We now populate the cropParams_ map in the loop where we handle
> > > the output stream configuration items. The key of this map is the index
> > > of the stream configuration structure set by the application. This will
> > > also be the same index used to specify the crop rectangles through the
> > > ScalerCrops control.
> > >
> > > CameraData::applyScalerCrop() has been adapted to look at either
> > > controls::ScalerCrop or controls::rpi::ScalerCrops. The former takes
> > > priority over the latter, and if present, will apply the same scaler
> > > crop to all output streams.

This should be documented in the controls::rpi::ScalerCrops definition.

> > >
> > > Finally return all crops through the same ScalerCrops control via
> > > request metadata. The first configure stream's crop rectangle is also
> > > returned via the ScalerCrop control in the request metadata.
> > >
> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > ---
> > >  .../pipeline/rpi/common/pipeline_base.cpp     | 69 +++++++++++++++----
> > >  1 file changed, 56 insertions(+), 13 deletions(-)
> > >
> > > diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> > > index a6ea4e9c47dd..b9759b682f0d 100644
> > > --- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> > > +++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
> > > @@ -561,11 +561,28 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
> > >       for (auto const &c : result.controlInfo)
> > >               ctrlMap.emplace(c.first, c.second);
> > >
> > > -     if (data->cropParams_.count(0)) {
> > > -             /* Add the ScalerCrop control limits based on the current mode. */
> > > +     if (data->cropParams_.size()) {
> >
> > This comment applies to the previous patch, but is there a case where
> > data->cropParams_ is not populated at all ?
> 
> Both vc4 and pisp will populate this.  I was being a bit too cautious
> here perhaps.  I'll remove the check.
> 
> > > +             /*
> > > +              * Add the ScalerCrop control limits based on the current mode and
> > > +              * the first configured stream.
> > > +              */
> > >               Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(data->cropParams_[0].ispMinCropSize));
> > >               ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,
> > >                                                            data->scaleIspCrop(data->cropParams_[0].ispCrop));
> >
> > empty line maybe ?
> 
> ack
> 
> > > +             if (data->cropParams_.size() == 2) {
> > > +                     /*
> > > +                      * The control map for rpi::ScalerCrops has the min value
> > > +                      * as the default crop for stream 0, max value as the default
> > > +                      * value for stream 1.
> >
> > mmm, I guess this is some internal agreement between pipeline_base and
> > pipeline_pisp that initializes cropParams in this way, right ?

Could this be handled in a bit cleaner way ?

> > > +                      */
> > > +                     ctrlMap[&controls::rpi::ScalerCrops] =
> > > +                             ControlInfo(data->scaleIspCrop(data->cropParams_[0].ispCrop),
> > > +                                         data->scaleIspCrop(data->cropParams_[1].ispCrop),
> > > +                                         ctrlMap[&controls::ScalerCrop].def());
> > > +             } else {
> > > +                     /* Match the same ControlInfo for rpi::ScalerCrops. */
> > > +                     ctrlMap[&controls::rpi::ScalerCrops] = ctrlMap[&controls::ScalerCrop];
> > > +             }
> > >       }
> > >
> > >       data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
> > > @@ -1292,10 +1309,29 @@ Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const
> > >
> > >  void CameraData::applyScalerCrop(const ControlList &controls)
> > >  {
> > > -     const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);
> > > -     if (scalerCrop && cropParams_.count(0)) {
> > > -             CropParams &cropParams = cropParams_[0];
> > > -             Rectangle nativeCrop = *scalerCrop;
> > > +     const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);
> > > +     const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);
> >
> > can both be specified in the same requests ? Does the usage of
> > ScalerCrops make ScalerCrop invalid ?
> 
> If they are both specified in the same request,
> controls::rpi::ScalerCrops will be used instead of
> controls::ScalerCrop.

This contradicts the commit message which specifies the opposite order.

> > It feels to me like pips should only register ScalerCrops and vc4 only
> > ScalerCrop. Yes, the application has to adapt to this in this case,
> > not sure how bad that would be
> 
> I think that would be workable.
> 
> > > +     std::vector<Rectangle> scalerCrops;
> > > +
> > > +     /*
> > > +      * First thing to do is create a vector of crops to apply to each ISP output
> > > +      * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if
> > > +      * present.
> > > +      *
> > > +      * If controls::ScalerCrop is present, apply the same crop to all ISP output
> > > +      * streams. Otherwise if controls::rpi::ScalerCrops, apply the given crops
> > > +      * to the ISP output streams, indexed by the same order in which they had
> > > +      * been configured. This is not the same as the ISP output index.
> > > +      */
> > > +     for (unsigned int i = 0; i < cropParams_.size(); i++) {
> > > +             if (scalerCropRPi && i < scalerCropRPi->size())
> > > +                     scalerCrops.push_back(scalerCropRPi->data()[i]);
> > > +             else if (scalerCropCore)
> > > +                     scalerCrops.push_back(*scalerCropCore);
> > > +     }
> > > +
> > > +     for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {
> > > +             Rectangle nativeCrop = scalerCrop;
> > >
> > >               if (!nativeCrop.width || !nativeCrop.height)
> > >                       nativeCrop = { 0, 0, 1, 1 };
> > > @@ -1311,13 +1347,13 @@ void CameraData::applyScalerCrop(const ControlList &controls)
> > >                * 2. With the same mid-point, if possible.
> > >                * 3. But it can't go outside the sensor area.
> > >                */
> > > -             Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
> > > +             Size minSize = cropParams_[i].ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
> > >               Size size = ispCrop.size().expandedTo(minSize);
> > >               ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));
> > >
> > > -             if (ispCrop != cropParams.ispCrop) {
> > > -                     cropParams.ispCrop = ispCrop;
> > > -                     platformSetIspCrop(cropParams.ispIndex, ispCrop);
> > > +             if (ispCrop != cropParams_[i].ispCrop) {
> > > +                     cropParams_[i].ispCrop = ispCrop;
> > > +                     platformSetIspCrop(cropParams_[i].ispIndex, ispCrop);
> >
> > You can really just pass cropsParams_[i] to platformSetIspCrop. It
> > contains the index and the updated ispCrop.
> 
> Ack.
> 
> > >               }
> > >       }
> > >  }
> > > @@ -1474,9 +1510,16 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request
> > >       request->metadata().set(controls::SensorTimestamp,
> > >                               bufferControls.get(controls::SensorTimestamp).value_or(0));
> > >
> > > -     if (cropParams_.count(0))
> > > -             request->metadata().set(controls::ScalerCrop,
> > > -                                     scaleIspCrop(cropParams_[0].ispCrop));
> > > +     if (cropParams_.size()) {
> >
> > Again I might have missed in which case cropParams_ is not populated
> > at all
> 
> As above, I can remove the check.
> 
> > > +             std::vector<Rectangle> crops;
> > > +
> > > +             for (auto const &[k, v] : cropParams_)
> > > +                     crops.push_back(scaleIspCrop(v.ispCrop));
> > > +
> > > +             request->metadata().set(controls::ScalerCrop, crops[0]);
> > > +             request->metadata().set(controls::rpi::ScalerCrops,
> > > +                                     Span<const Rectangle>(crops.data(), crops.size()));
> > > +     }
> > >  }
> > >
> > >  } /* namespace libcamera */

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
index a6ea4e9c47dd..b9759b682f0d 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -561,11 +561,28 @@  int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)
 	for (auto const &c : result.controlInfo)
 		ctrlMap.emplace(c.first, c.second);
 
-	if (data->cropParams_.count(0)) {
-		/* Add the ScalerCrop control limits based on the current mode. */
+	if (data->cropParams_.size()) {
+		/*
+		 * Add the ScalerCrop control limits based on the current mode and
+		 * the first configured stream.
+		 */
 		Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(data->cropParams_[0].ispMinCropSize));
 		ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop,
 							     data->scaleIspCrop(data->cropParams_[0].ispCrop));
+		if (data->cropParams_.size() == 2) {
+			/*
+			 * The control map for rpi::ScalerCrops has the min value
+			 * as the default crop for stream 0, max value as the default
+			 * value for stream 1.
+			 */
+			ctrlMap[&controls::rpi::ScalerCrops] =
+				ControlInfo(data->scaleIspCrop(data->cropParams_[0].ispCrop),
+					    data->scaleIspCrop(data->cropParams_[1].ispCrop),
+					    ctrlMap[&controls::ScalerCrop].def());
+		} else {
+			/* Match the same ControlInfo for rpi::ScalerCrops. */
+			ctrlMap[&controls::rpi::ScalerCrops] = ctrlMap[&controls::ScalerCrop];
+		}
 	}
 
 	data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap());
@@ -1292,10 +1309,29 @@  Rectangle CameraData::scaleIspCrop(const Rectangle &ispCrop) const
 
 void CameraData::applyScalerCrop(const ControlList &controls)
 {
-	const auto &scalerCrop = controls.get<Rectangle>(controls::ScalerCrop);
-	if (scalerCrop && cropParams_.count(0)) {
-		CropParams &cropParams = cropParams_[0];
-		Rectangle nativeCrop = *scalerCrop;
+	const auto &scalerCropRPi = controls.get<Span<const Rectangle>>(controls::rpi::ScalerCrops);
+	const auto &scalerCropCore = controls.get<Rectangle>(controls::ScalerCrop);
+	std::vector<Rectangle> scalerCrops;
+
+	/*
+	 * First thing to do is create a vector of crops to apply to each ISP output
+	 * based on either controls::ScalerCrop or controls::rpi::ScalerCrops if
+	 * present.
+	 *
+	 * If controls::ScalerCrop is present, apply the same crop to all ISP output
+	 * streams. Otherwise if controls::rpi::ScalerCrops, apply the given crops
+	 * to the ISP output streams, indexed by the same order in which they had
+	 * been configured. This is not the same as the ISP output index.
+	 */
+	for (unsigned int i = 0; i < cropParams_.size(); i++) {
+		if (scalerCropRPi && i < scalerCropRPi->size())
+			scalerCrops.push_back(scalerCropRPi->data()[i]);
+		else if (scalerCropCore)
+			scalerCrops.push_back(*scalerCropCore);
+	}
+
+	for (auto const &[i, scalerCrop] : utils::enumerate(scalerCrops)) {
+		Rectangle nativeCrop = scalerCrop;
 
 		if (!nativeCrop.width || !nativeCrop.height)
 			nativeCrop = { 0, 0, 1, 1 };
@@ -1311,13 +1347,13 @@  void CameraData::applyScalerCrop(const ControlList &controls)
 		 * 2. With the same mid-point, if possible.
 		 * 3. But it can't go outside the sensor area.
 		 */
-		Size minSize = cropParams.ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
+		Size minSize = cropParams_[i].ispMinCropSize.expandedToAspectRatio(nativeCrop.size());
 		Size size = ispCrop.size().expandedTo(minSize);
 		ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize));
 
-		if (ispCrop != cropParams.ispCrop) {
-			cropParams.ispCrop = ispCrop;
-			platformSetIspCrop(cropParams.ispIndex, ispCrop);
+		if (ispCrop != cropParams_[i].ispCrop) {
+			cropParams_[i].ispCrop = ispCrop;
+			platformSetIspCrop(cropParams_[i].ispIndex, ispCrop);
 		}
 	}
 }
@@ -1474,9 +1510,16 @@  void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request
 	request->metadata().set(controls::SensorTimestamp,
 				bufferControls.get(controls::SensorTimestamp).value_or(0));
 
-	if (cropParams_.count(0))
-		request->metadata().set(controls::ScalerCrop,
-					scaleIspCrop(cropParams_[0].ispCrop));
+	if (cropParams_.size()) {
+		std::vector<Rectangle> crops;
+
+		for (auto const &[k, v] : cropParams_)
+			crops.push_back(scaleIspCrop(v.ispCrop));
+
+		request->metadata().set(controls::ScalerCrop, crops[0]);
+		request->metadata().set(controls::rpi::ScalerCrops,
+					Span<const Rectangle>(crops.data(), crops.size()));
+	}
 }
 
 } /* namespace libcamera */