[v2,6/8] libcamera: converter_v4l2_m2m: Improve crop bounds support
diff mbox series

Message ID 20241125151430.2437285-7-stefan.klug@ideasonboard.com
State Superseded
Headers show
Series
  • rkisp1: Fix aspect ratio and ScalerCrop
Related show

Commit Message

Stefan Klug Nov. 25, 2024, 3:14 p.m. UTC
When a converter (dw100 on imx8mp in this case) is used in a pipeline,
the ScalerCrop control get's created at createCamera() time. As this is
before configure(), no streams were configured on the converter and the
stream specific crop bounds are not known. Extend the converter class to
report the default crop bounds in case the provided stream is not found.

Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
---
Changes in v2:
- Added this patch
---
 .../internal/converter/converter_v4l2_m2m.h   |   1 +
 src/libcamera/converter.cpp                   |   3 +
 .../converter/converter_v4l2_m2m.cpp          | 113 +++++++++---------
 3 files changed, 59 insertions(+), 58 deletions(-)

Comments

Jacopo Mondi Nov. 25, 2024, 6:36 p.m. UTC | #1
On Mon, Nov 25, 2024 at 04:14:15PM +0100, Stefan Klug wrote:
> When a converter (dw100 on imx8mp in this case) is used in a pipeline,
> the ScalerCrop control get's created at createCamera() time. As this is

Kind of unrelated to this patch, right ? :)

> before configure(), no streams were configured on the converter and the
> stream specific crop bounds are not known. Extend the converter class to
> report the default crop bounds in case the provided stream is not found.
>
> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> ---
> Changes in v2:
> - Added this patch
> ---
>  .../internal/converter/converter_v4l2_m2m.h   |   1 +
>  src/libcamera/converter.cpp                   |   3 +
>  .../converter/converter_v4l2_m2m.cpp          | 113 +++++++++---------
>  3 files changed, 59 insertions(+), 58 deletions(-)
>
> diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> index 0bc0d053e2c4..a5286166f574 100644
> --- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
> +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> @@ -105,6 +105,7 @@ private:
>
>  	std::map<const Stream *, std::unique_ptr<V4L2M2MStream>> streams_;
>  	std::map<FrameBuffer *, unsigned int> queue_;
> +	std::pair<Rectangle, Rectangle> inputCropBounds_;
>  };
>
>  } /* namespace libcamera */
> diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp
> index 945f2527b96a..d4b5e5260e02 100644
> --- a/src/libcamera/converter.cpp
> +++ b/src/libcamera/converter.cpp
> @@ -195,6 +195,9 @@ Converter::~Converter()
>   * this function should be called after the \a stream has been configured using
>   * configure().
>   *
> + * When called with an invalid \a stream, the function returns the default crop
> + * bounds of the converter.
> + *
>   * \return A pair containing the minimum and maximum crop bound in that order
>   */
>
> diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
> index d63ef2f8028f..bd7e5cce600d 100644
> --- a/src/libcamera/converter/converter_v4l2_m2m.cpp
> +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
> @@ -30,6 +30,52 @@ namespace libcamera {
>
>  LOG_DECLARE_CATEGORY(Converter)
>
> +namespace {
> +
> +int getCropBounds(V4L2VideoDevice *device, Rectangle &minCrop,
> +		  Rectangle &maxCrop)
> +{
> +	Rectangle minC;
> +	Rectangle maxC;
> +
> +	/* Find crop bounds */
> +	minC.width = 1;
> +	minC.height = 1;
> +	maxC.width = UINT_MAX;
> +	maxC.height = UINT_MAX;
> +
> +	int ret = device->setSelection(V4L2_SEL_TGT_CROP, &minC);
> +	if (ret) {
> +		LOG(Converter, Error)
> +			<< "Could not query minimum selection crop: "
> +			<< strerror(-ret);
> +		return ret;
> +	}
> +
> +	ret = device->getSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxC);
> +	if (ret) {
> +		LOG(Converter, Error)
> +			<< "Could not query maximum selection crop: "
> +			<< strerror(-ret);
> +		return ret;
> +	}
> +
> +	/* Reset the crop to its maximum */
> +	ret = device->setSelection(V4L2_SEL_TGT_CROP, &maxC);
> +	if (ret) {
> +		LOG(Converter, Error)
> +			<< "Could not reset selection crop: "
> +			<< strerror(-ret);
> +		return ret;
> +	}
> +
> +	minCrop = minC;
> +	maxCrop = maxC;
> +	return 0;
> +}
> +
> +} /* namespace */
> +
>  /* -----------------------------------------------------------------------------
>   * V4L2M2MConverter::V4L2M2MStream
>   */
> @@ -98,41 +144,10 @@ int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputC
>  	outputBufferCount_ = outputCfg.bufferCount;
>
>  	if (converter_->features() & Feature::InputCrop) {
> -		Rectangle minCrop;
> -		Rectangle maxCrop;
> -
> -		/* Find crop bounds */
> -		minCrop.width = 1;
> -		minCrop.height = 1;
> -		maxCrop.width = UINT_MAX;
> -		maxCrop.height = UINT_MAX;
> -
> -		ret = setInputSelection(V4L2_SEL_TGT_CROP, &minCrop);
> -		if (ret) {
> -			LOG(Converter, Error)
> -				<< "Could not query minimum selection crop: "
> -				<< strerror(-ret);
> -			return ret;
> -		}
> -
> -		ret = getInputSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxCrop);
> -		if (ret) {
> -			LOG(Converter, Error)
> -				<< "Could not query maximum selection crop: "
> -				<< strerror(-ret);
> -			return ret;
> -		}
> -
> -		/* Reset the crop to its maximum */
> -		ret = setInputSelection(V4L2_SEL_TGT_CROP, &maxCrop);
> -		if (ret) {
> -			LOG(Converter, Error)
> -				<< "Could not reset selection crop: "
> -				<< strerror(-ret);

Maybe I don't understand M2M enough, but is there of Stream-specific
here ?

There's an output and an input queue, right ? Do they have per-stream
crop bounds ??

> +		ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
> +				    inputCropBounds_.second);
> +		if (ret)
>  			return ret;
> -		}
> -
> -		inputCropBounds_ = { minCrop, maxCrop };
>  	}
>
>  	return 0;
> @@ -258,27 +273,9 @@ V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)
>  		return;
>  	}
>
> -	/* Discover Feature::InputCrop */
> -	Rectangle maxCrop;
> -	maxCrop.width = UINT_MAX;
> -	maxCrop.height = UINT_MAX;
> -
> -	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &maxCrop);
> -	if (ret)
> -		return;
> -
> -	/*
> -	 * Rectangles for cropping targets are defined even if the device
> -	 * does not support cropping. Their sizes and positions will be
> -	 * fixed in such cases.
> -	 *
> -	 * Set and inspect a crop equivalent to half of the maximum crop
> -	 * returned earlier. Use this to determine whether the crop on
> -	 * input is really supported.
> -	 */
> -	Rectangle halfCrop(maxCrop.size() / 2);
> -	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &halfCrop);
> -	if (!ret && halfCrop != maxCrop) {
> +	ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
> +			    inputCropBounds_.second);
> +	if (!ret && inputCropBounds_.first != inputCropBounds_.second) {
>  		features_ |= Feature::InputCrop;
>
>  		LOG(Converter, Info)
> @@ -477,10 +474,10 @@ std::pair<Rectangle, Rectangle>
>  V4L2M2MConverter::inputCropBounds(const Stream *stream)
>  {
>  	auto iter = streams_.find(stream);
> -	if (iter == streams_.end())
> -		return {};
> +	if (iter != streams_.end())
> +		return iter->second->inputCropBounds();
>
> -	return iter->second->inputCropBounds();
> +	return inputCropBounds_;
>  }
>
>  /**
> --
> 2.43.0
>
Stefan Klug Nov. 28, 2024, 1:04 p.m. UTC | #2
On Mon, Nov 25, 2024 at 07:36:50PM +0100, Jacopo Mondi wrote:
> On Mon, Nov 25, 2024 at 04:14:15PM +0100, Stefan Klug wrote:
> > When a converter (dw100 on imx8mp in this case) is used in a pipeline,
> > the ScalerCrop control get's created at createCamera() time. As this is
> 
> Kind of unrelated to this patch, right ? :)

A bit maybe. Without it, I would ask myself: Why would anyone ask for
cropBounds before configuring the coverter?

How would you phrase that?

> 
> > before configure(), no streams were configured on the converter and the
> > stream specific crop bounds are not known. Extend the converter class to
> > report the default crop bounds in case the provided stream is not found.
> >
> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> > ---
> > Changes in v2:
> > - Added this patch
> > ---
> >  .../internal/converter/converter_v4l2_m2m.h   |   1 +
> >  src/libcamera/converter.cpp                   |   3 +
> >  .../converter/converter_v4l2_m2m.cpp          | 113 +++++++++---------
> >  3 files changed, 59 insertions(+), 58 deletions(-)
> >
> > diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> > index 0bc0d053e2c4..a5286166f574 100644
> > --- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
> > +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> > @@ -105,6 +105,7 @@ private:
> >
> >  	std::map<const Stream *, std::unique_ptr<V4L2M2MStream>> streams_;
> >  	std::map<FrameBuffer *, unsigned int> queue_;
> > +	std::pair<Rectangle, Rectangle> inputCropBounds_;
> >  };
> >
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp
> > index 945f2527b96a..d4b5e5260e02 100644
> > --- a/src/libcamera/converter.cpp
> > +++ b/src/libcamera/converter.cpp
> > @@ -195,6 +195,9 @@ Converter::~Converter()
> >   * this function should be called after the \a stream has been configured using
> >   * configure().
> >   *
> > + * When called with an invalid \a stream, the function returns the default crop
> > + * bounds of the converter.
> > + *
> >   * \return A pair containing the minimum and maximum crop bound in that order
> >   */
> >
> > diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
> > index d63ef2f8028f..bd7e5cce600d 100644
> > --- a/src/libcamera/converter/converter_v4l2_m2m.cpp
> > +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
> > @@ -30,6 +30,52 @@ namespace libcamera {
> >
> >  LOG_DECLARE_CATEGORY(Converter)
> >
> > +namespace {
> > +
> > +int getCropBounds(V4L2VideoDevice *device, Rectangle &minCrop,
> > +		  Rectangle &maxCrop)
> > +{
> > +	Rectangle minC;
> > +	Rectangle maxC;
> > +
> > +	/* Find crop bounds */
> > +	minC.width = 1;
> > +	minC.height = 1;
> > +	maxC.width = UINT_MAX;
> > +	maxC.height = UINT_MAX;
> > +
> > +	int ret = device->setSelection(V4L2_SEL_TGT_CROP, &minC);
> > +	if (ret) {
> > +		LOG(Converter, Error)
> > +			<< "Could not query minimum selection crop: "
> > +			<< strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	ret = device->getSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxC);
> > +	if (ret) {
> > +		LOG(Converter, Error)
> > +			<< "Could not query maximum selection crop: "
> > +			<< strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	/* Reset the crop to its maximum */
> > +	ret = device->setSelection(V4L2_SEL_TGT_CROP, &maxC);
> > +	if (ret) {
> > +		LOG(Converter, Error)
> > +			<< "Could not reset selection crop: "
> > +			<< strerror(-ret);
> > +		return ret;
> > +	}
> > +
> > +	minCrop = minC;
> > +	maxCrop = maxC;
> > +	return 0;
> > +}
> > +
> > +} /* namespace */
> > +
> >  /* -----------------------------------------------------------------------------
> >   * V4L2M2MConverter::V4L2M2MStream
> >   */
> > @@ -98,41 +144,10 @@ int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputC
> >  	outputBufferCount_ = outputCfg.bufferCount;
> >
> >  	if (converter_->features() & Feature::InputCrop) {
> > -		Rectangle minCrop;
> > -		Rectangle maxCrop;
> > -
> > -		/* Find crop bounds */
> > -		minCrop.width = 1;
> > -		minCrop.height = 1;
> > -		maxCrop.width = UINT_MAX;
> > -		maxCrop.height = UINT_MAX;
> > -
> > -		ret = setInputSelection(V4L2_SEL_TGT_CROP, &minCrop);
> > -		if (ret) {
> > -			LOG(Converter, Error)
> > -				<< "Could not query minimum selection crop: "
> > -				<< strerror(-ret);
> > -			return ret;
> > -		}
> > -
> > -		ret = getInputSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxCrop);
> > -		if (ret) {
> > -			LOG(Converter, Error)
> > -				<< "Could not query maximum selection crop: "
> > -				<< strerror(-ret);
> > -			return ret;
> > -		}
> > -
> > -		/* Reset the crop to its maximum */
> > -		ret = setInputSelection(V4L2_SEL_TGT_CROP, &maxCrop);
> > -		if (ret) {
> > -			LOG(Converter, Error)
> > -				<< "Could not reset selection crop: "
> > -				<< strerror(-ret);
> 
> Maybe I don't understand M2M enough, but is there of Stream-specific
> here ?
> 
> There's an output and an input queue, right ? Do they have per-stream
> crop bounds ??

I think I don't get the question here. Yes, every context/stream has own
crop bounds. What is wrong with that? Or did we settle that in the
discussions?

Cheers,
Stefan

> 
> > +		ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
> > +				    inputCropBounds_.second);
> > +		if (ret)
> >  			return ret;
> > -		}
> > -
> > -		inputCropBounds_ = { minCrop, maxCrop };
> >  	}
> >
> >  	return 0;
> > @@ -258,27 +273,9 @@ V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)
> >  		return;
> >  	}
> >
> > -	/* Discover Feature::InputCrop */
> > -	Rectangle maxCrop;
> > -	maxCrop.width = UINT_MAX;
> > -	maxCrop.height = UINT_MAX;
> > -
> > -	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &maxCrop);
> > -	if (ret)
> > -		return;
> > -
> > -	/*
> > -	 * Rectangles for cropping targets are defined even if the device
> > -	 * does not support cropping. Their sizes and positions will be
> > -	 * fixed in such cases.
> > -	 *
> > -	 * Set and inspect a crop equivalent to half of the maximum crop
> > -	 * returned earlier. Use this to determine whether the crop on
> > -	 * input is really supported.
> > -	 */
> > -	Rectangle halfCrop(maxCrop.size() / 2);
> > -	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &halfCrop);
> > -	if (!ret && halfCrop != maxCrop) {
> > +	ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
> > +			    inputCropBounds_.second);
> > +	if (!ret && inputCropBounds_.first != inputCropBounds_.second) {
> >  		features_ |= Feature::InputCrop;
> >
> >  		LOG(Converter, Info)
> > @@ -477,10 +474,10 @@ std::pair<Rectangle, Rectangle>
> >  V4L2M2MConverter::inputCropBounds(const Stream *stream)
> >  {
> >  	auto iter = streams_.find(stream);
> > -	if (iter == streams_.end())
> > -		return {};
> > +	if (iter != streams_.end())
> > +		return iter->second->inputCropBounds();
> >
> > -	return iter->second->inputCropBounds();
> > +	return inputCropBounds_;
> >  }
> >
> >  /**
> > --
> > 2.43.0
> >
Jacopo Mondi Nov. 28, 2024, 1:29 p.m. UTC | #3
Hi Stefan

On Thu, Nov 28, 2024 at 02:04:12PM +0100, Stefan Klug wrote:
> On Mon, Nov 25, 2024 at 07:36:50PM +0100, Jacopo Mondi wrote:
> > On Mon, Nov 25, 2024 at 04:14:15PM +0100, Stefan Klug wrote:
> > > When a converter (dw100 on imx8mp in this case) is used in a pipeline,
> > > the ScalerCrop control get's created at createCamera() time. As this is
> >
> > Kind of unrelated to this patch, right ? :)
>
> A bit maybe. Without it, I would ask myself: Why would anyone ask for
> cropBounds before configuring the coverter?
>
> How would you phrase that?
>

Something like:

The inputCropBounds_ member of the V4L2M2MConverter::Stream class is only
initialized after a V4L2M2MConverter::configure() call, when the
streams are initialized.

However, the converter has crop limits that do not depend on the
configured Streams, and which are currently not accessible from the
class interface.

Add a new inputCropBounds() function to the V4L2M2MConverter class
that allows to retrieve the converter crop limits before any stream
is configured.

This is particularly useful for pipelines to initialize controls and
properties and to implement validation before the Camera gets
configured.

> >
> > > before configure(), no streams were configured on the converter and the
> > > stream specific crop bounds are not known. Extend the converter class to
> > > report the default crop bounds in case the provided stream is not found.
> > >
> > > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>
> > > ---
> > > Changes in v2:
> > > - Added this patch
> > > ---
> > >  .../internal/converter/converter_v4l2_m2m.h   |   1 +
> > >  src/libcamera/converter.cpp                   |   3 +
> > >  .../converter/converter_v4l2_m2m.cpp          | 113 +++++++++---------
> > >  3 files changed, 59 insertions(+), 58 deletions(-)
> > >
> > > diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> > > index 0bc0d053e2c4..a5286166f574 100644
> > > --- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
> > > +++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
> > > @@ -105,6 +105,7 @@ private:
> > >
> > >  	std::map<const Stream *, std::unique_ptr<V4L2M2MStream>> streams_;
> > >  	std::map<FrameBuffer *, unsigned int> queue_;
> > > +	std::pair<Rectangle, Rectangle> inputCropBounds_;
> > >  };
> > >
> > >  } /* namespace libcamera */
> > > diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp
> > > index 945f2527b96a..d4b5e5260e02 100644
> > > --- a/src/libcamera/converter.cpp
> > > +++ b/src/libcamera/converter.cpp
> > > @@ -195,6 +195,9 @@ Converter::~Converter()
> > >   * this function should be called after the \a stream has been configured using
> > >   * configure().
> > >   *
> > > + * When called with an invalid \a stream, the function returns the default crop
> > > + * bounds of the converter.
> > > + *

Currently the pipeline does

        dewarper_->inputCropBounds(&data->mainPathStream_);

However we know that there's no stream (yet) in the V4L2M2MConverter
class that is associated with &data->mainPathStream_.

I wonder if V4L2M2MConverter::inputCropBounds() (without any
parameter) would be better to express that we want the absolute limits
for the device (regardless of the configured streams).

Yes, it would require the pipeline to keep track of "Is the dewarper
configured or not" which would make things a little more complicated
as in example updateControls() is called before and after the
configuration.

We could add a function to the Converter class that give a Stream *
retrieves if it's configured or not... Too clunky ?


> > >   * \return A pair containing the minimum and maximum crop bound in that order
> > >   */
> > >
> > > diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
> > > index d63ef2f8028f..bd7e5cce600d 100644
> > > --- a/src/libcamera/converter/converter_v4l2_m2m.cpp
> > > +++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
> > > @@ -30,6 +30,52 @@ namespace libcamera {
> > >
> > >  LOG_DECLARE_CATEGORY(Converter)
> > >
> > > +namespace {
> > > +
> > > +int getCropBounds(V4L2VideoDevice *device, Rectangle &minCrop,
> > > +		  Rectangle &maxCrop)

Please not that all this function needs is a video device *

So it could be made a private member of the V4L2M2MConverter class and
called from the Stream which has a pointer to converter_.

This way you could easily implement:
V4L2M2MConverter::inputCropBounds() and
V4L2M2MConverter::outputCropBounds().

(Do outputCropBounds() make sense at all ?)


> > > +{
> > > +	Rectangle minC;
> > > +	Rectangle maxC;
> > > +
> > > +	/* Find crop bounds */
> > > +	minC.width = 1;
> > > +	minC.height = 1;
> > > +	maxC.width = UINT_MAX;
> > > +	maxC.height = UINT_MAX;
> > > +
> > > +	int ret = device->setSelection(V4L2_SEL_TGT_CROP, &minC);
> > > +	if (ret) {
> > > +		LOG(Converter, Error)
> > > +			<< "Could not query minimum selection crop: "
> > > +			<< strerror(-ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = device->getSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxC);
> > > +	if (ret) {
> > > +		LOG(Converter, Error)
> > > +			<< "Could not query maximum selection crop: "
> > > +			<< strerror(-ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	/* Reset the crop to its maximum */
> > > +	ret = device->setSelection(V4L2_SEL_TGT_CROP, &maxC);
> > > +	if (ret) {
> > > +		LOG(Converter, Error)
> > > +			<< "Could not reset selection crop: "
> > > +			<< strerror(-ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	minCrop = minC;
> > > +	maxCrop = maxC;
> > > +	return 0;
> > > +}
> > > +
> > > +} /* namespace */
> > > +
> > >  /* -----------------------------------------------------------------------------
> > >   * V4L2M2MConverter::V4L2M2MStream
> > >   */
> > > @@ -98,41 +144,10 @@ int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputC
> > >  	outputBufferCount_ = outputCfg.bufferCount;
> > >
> > >  	if (converter_->features() & Feature::InputCrop) {
> > > -		Rectangle minCrop;
> > > -		Rectangle maxCrop;
> > > -
> > > -		/* Find crop bounds */
> > > -		minCrop.width = 1;
> > > -		minCrop.height = 1;
> > > -		maxCrop.width = UINT_MAX;
> > > -		maxCrop.height = UINT_MAX;
> > > -
> > > -		ret = setInputSelection(V4L2_SEL_TGT_CROP, &minCrop);
> > > -		if (ret) {
> > > -			LOG(Converter, Error)
> > > -				<< "Could not query minimum selection crop: "
> > > -				<< strerror(-ret);
> > > -			return ret;
> > > -		}
> > > -
> > > -		ret = getInputSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxCrop);
> > > -		if (ret) {
> > > -			LOG(Converter, Error)
> > > -				<< "Could not query maximum selection crop: "
> > > -				<< strerror(-ret);
> > > -			return ret;
> > > -		}
> > > -
> > > -		/* Reset the crop to its maximum */
> > > -		ret = setInputSelection(V4L2_SEL_TGT_CROP, &maxCrop);
> > > -		if (ret) {
> > > -			LOG(Converter, Error)
> > > -				<< "Could not reset selection crop: "
> > > -				<< strerror(-ret);
> >
> > Maybe I don't understand M2M enough, but is there of Stream-specific
> > here ?
> >
> > There's an output and an input queue, right ? Do they have per-stream
> > crop bounds ??
>
> I think I don't get the question here. Yes, every context/stream has own
> crop bounds. What is wrong with that? Or did we settle that in the
> discussions?

Yeah, thanks, it has been clarified. When there's a Stream with a
format applied, the CROP_BOUNDS are relative to that format.

Thanks
  j

>
> Cheers,
> Stefan
>
> >
> > > +		ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
> > > +				    inputCropBounds_.second);
> > > +		if (ret)
> > >  			return ret;
> > > -		}
> > > -
> > > -		inputCropBounds_ = { minCrop, maxCrop };
> > >  	}
> > >
> > >  	return 0;
> > > @@ -258,27 +273,9 @@ V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)
> > >  		return;
> > >  	}
> > >
> > > -	/* Discover Feature::InputCrop */
> > > -	Rectangle maxCrop;
> > > -	maxCrop.width = UINT_MAX;
> > > -	maxCrop.height = UINT_MAX;
> > > -
> > > -	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &maxCrop);
> > > -	if (ret)
> > > -		return;
> > > -
> > > -	/*
> > > -	 * Rectangles for cropping targets are defined even if the device
> > > -	 * does not support cropping. Their sizes and positions will be
> > > -	 * fixed in such cases.
> > > -	 *
> > > -	 * Set and inspect a crop equivalent to half of the maximum crop
> > > -	 * returned earlier. Use this to determine whether the crop on
> > > -	 * input is really supported.
> > > -	 */
> > > -	Rectangle halfCrop(maxCrop.size() / 2);
> > > -	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &halfCrop);
> > > -	if (!ret && halfCrop != maxCrop) {
> > > +	ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
> > > +			    inputCropBounds_.second);
> > > +	if (!ret && inputCropBounds_.first != inputCropBounds_.second) {
> > >  		features_ |= Feature::InputCrop;
> > >
> > >  		LOG(Converter, Info)
> > > @@ -477,10 +474,10 @@ std::pair<Rectangle, Rectangle>
> > >  V4L2M2MConverter::inputCropBounds(const Stream *stream)
> > >  {
> > >  	auto iter = streams_.find(stream);
> > > -	if (iter == streams_.end())
> > > -		return {};
> > > +	if (iter != streams_.end())
> > > +		return iter->second->inputCropBounds();
> > >
> > > -	return iter->second->inputCropBounds();
> > > +	return inputCropBounds_;
> > >  }
> > >
> > >  /**
> > > --
> > > 2.43.0
> > >

Patch
diff mbox series

diff --git a/include/libcamera/internal/converter/converter_v4l2_m2m.h b/include/libcamera/internal/converter/converter_v4l2_m2m.h
index 0bc0d053e2c4..a5286166f574 100644
--- a/include/libcamera/internal/converter/converter_v4l2_m2m.h
+++ b/include/libcamera/internal/converter/converter_v4l2_m2m.h
@@ -105,6 +105,7 @@  private:
 
 	std::map<const Stream *, std::unique_ptr<V4L2M2MStream>> streams_;
 	std::map<FrameBuffer *, unsigned int> queue_;
+	std::pair<Rectangle, Rectangle> inputCropBounds_;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/converter.cpp b/src/libcamera/converter.cpp
index 945f2527b96a..d4b5e5260e02 100644
--- a/src/libcamera/converter.cpp
+++ b/src/libcamera/converter.cpp
@@ -195,6 +195,9 @@  Converter::~Converter()
  * this function should be called after the \a stream has been configured using
  * configure().
  *
+ * When called with an invalid \a stream, the function returns the default crop
+ * bounds of the converter.
+ *
  * \return A pair containing the minimum and maximum crop bound in that order
  */
 
diff --git a/src/libcamera/converter/converter_v4l2_m2m.cpp b/src/libcamera/converter/converter_v4l2_m2m.cpp
index d63ef2f8028f..bd7e5cce600d 100644
--- a/src/libcamera/converter/converter_v4l2_m2m.cpp
+++ b/src/libcamera/converter/converter_v4l2_m2m.cpp
@@ -30,6 +30,52 @@  namespace libcamera {
 
 LOG_DECLARE_CATEGORY(Converter)
 
+namespace {
+
+int getCropBounds(V4L2VideoDevice *device, Rectangle &minCrop,
+		  Rectangle &maxCrop)
+{
+	Rectangle minC;
+	Rectangle maxC;
+
+	/* Find crop bounds */
+	minC.width = 1;
+	minC.height = 1;
+	maxC.width = UINT_MAX;
+	maxC.height = UINT_MAX;
+
+	int ret = device->setSelection(V4L2_SEL_TGT_CROP, &minC);
+	if (ret) {
+		LOG(Converter, Error)
+			<< "Could not query minimum selection crop: "
+			<< strerror(-ret);
+		return ret;
+	}
+
+	ret = device->getSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxC);
+	if (ret) {
+		LOG(Converter, Error)
+			<< "Could not query maximum selection crop: "
+			<< strerror(-ret);
+		return ret;
+	}
+
+	/* Reset the crop to its maximum */
+	ret = device->setSelection(V4L2_SEL_TGT_CROP, &maxC);
+	if (ret) {
+		LOG(Converter, Error)
+			<< "Could not reset selection crop: "
+			<< strerror(-ret);
+		return ret;
+	}
+
+	minCrop = minC;
+	maxCrop = maxC;
+	return 0;
+}
+
+} /* namespace */
+
 /* -----------------------------------------------------------------------------
  * V4L2M2MConverter::V4L2M2MStream
  */
@@ -98,41 +144,10 @@  int V4L2M2MConverter::V4L2M2MStream::configure(const StreamConfiguration &inputC
 	outputBufferCount_ = outputCfg.bufferCount;
 
 	if (converter_->features() & Feature::InputCrop) {
-		Rectangle minCrop;
-		Rectangle maxCrop;
-
-		/* Find crop bounds */
-		minCrop.width = 1;
-		minCrop.height = 1;
-		maxCrop.width = UINT_MAX;
-		maxCrop.height = UINT_MAX;
-
-		ret = setInputSelection(V4L2_SEL_TGT_CROP, &minCrop);
-		if (ret) {
-			LOG(Converter, Error)
-				<< "Could not query minimum selection crop: "
-				<< strerror(-ret);
-			return ret;
-		}
-
-		ret = getInputSelection(V4L2_SEL_TGT_CROP_BOUNDS, &maxCrop);
-		if (ret) {
-			LOG(Converter, Error)
-				<< "Could not query maximum selection crop: "
-				<< strerror(-ret);
-			return ret;
-		}
-
-		/* Reset the crop to its maximum */
-		ret = setInputSelection(V4L2_SEL_TGT_CROP, &maxCrop);
-		if (ret) {
-			LOG(Converter, Error)
-				<< "Could not reset selection crop: "
-				<< strerror(-ret);
+		ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
+				    inputCropBounds_.second);
+		if (ret)
 			return ret;
-		}
-
-		inputCropBounds_ = { minCrop, maxCrop };
 	}
 
 	return 0;
@@ -258,27 +273,9 @@  V4L2M2MConverter::V4L2M2MConverter(MediaDevice *media)
 		return;
 	}
 
-	/* Discover Feature::InputCrop */
-	Rectangle maxCrop;
-	maxCrop.width = UINT_MAX;
-	maxCrop.height = UINT_MAX;
-
-	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &maxCrop);
-	if (ret)
-		return;
-
-	/*
-	 * Rectangles for cropping targets are defined even if the device
-	 * does not support cropping. Their sizes and positions will be
-	 * fixed in such cases.
-	 *
-	 * Set and inspect a crop equivalent to half of the maximum crop
-	 * returned earlier. Use this to determine whether the crop on
-	 * input is really supported.
-	 */
-	Rectangle halfCrop(maxCrop.size() / 2);
-	ret = m2m_->output()->setSelection(V4L2_SEL_TGT_CROP, &halfCrop);
-	if (!ret && halfCrop != maxCrop) {
+	ret = getCropBounds(m2m_->output(), inputCropBounds_.first,
+			    inputCropBounds_.second);
+	if (!ret && inputCropBounds_.first != inputCropBounds_.second) {
 		features_ |= Feature::InputCrop;
 
 		LOG(Converter, Info)
@@ -477,10 +474,10 @@  std::pair<Rectangle, Rectangle>
 V4L2M2MConverter::inputCropBounds(const Stream *stream)
 {
 	auto iter = streams_.find(stream);
-	if (iter == streams_.end())
-		return {};
+	if (iter != streams_.end())
+		return iter->second->inputCropBounds();
 
-	return iter->second->inputCropBounds();
+	return inputCropBounds_;
 }
 
 /**