[libcamera-devel,v9,03/18] libcamera: framebuffer_allocator: Make allocate() require count
diff mbox series

Message ID 20221216122939.256534-4-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • lc-compliance: Add test to queue more requests than hardware depth
Related show

Commit Message

Paul Elder Dec. 16, 2022, 12:29 p.m. UTC
From: Nícolas F. R. A. Prado <nfraprado@collabora.com>

Make FrameBufferAllocator::allocate() require a 'count' argument for the
number of buffers to be allocated.

Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v9:
- rebased
- notably, ControlList::get() returns a std::optional, and since the
  MinimumRequests property is a required property, we can simply
  get it via .value()
  - this will be enforced by lc-compliance in a later patch

Changes in v8:
- Updated application-developer and pipeline-handler guides with new allocate()
  API and MinimumRequests property
- Added handling for when allocate() returns less buffers than needed in cam and
  the capture unit test
- Improved FrameBufferAllocator::allocate() description

Changes in v6:
- Changed static_cast to convert 'count' to unsigned int instead of
  'bufferCount' to int when comparing

Changes in v5:
- Made sure that qcam allocates at least 2 buffers
---
 Documentation/guides/application-developer.rst     |  9 +++++++--
 Documentation/guides/pipeline-handler.rst          |  3 +--
 include/libcamera/camera.h                         |  2 +-
 include/libcamera/framebuffer_allocator.h          |  2 +-
 include/libcamera/internal/pipeline_handler.h      |  2 +-
 src/apps/cam/camera_session.cpp                    | 13 +++++++++----
 src/apps/lc-compliance/simple_capture.cpp          |  8 ++++++--
 src/apps/qcam/main_window.cpp                      | 10 +++++++++-
 src/gstreamer/gstlibcameraallocator.cpp            |  5 ++++-
 src/libcamera/camera.cpp                           |  4 ++--
 src/libcamera/framebuffer_allocator.cpp            |  9 +++++++--
 src/libcamera/pipeline/ipu3/ipu3.cpp               |  4 ++--
 src/libcamera/pipeline/raspberrypi/raspberrypi.cpp |  4 ++--
 src/libcamera/pipeline/rkisp1/rkisp1.cpp           |  4 ++--
 src/libcamera/pipeline/simple/simple.cpp           |  4 ++--
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp       |  7 ++++---
 src/libcamera/pipeline/vimc/vimc.cpp               |  7 ++++---
 src/libcamera/pipeline_handler.cpp                 |  1 +
 src/v4l2/v4l2_camera.cpp                           |  2 +-
 test/camera/camera_reconfigure.cpp                 |  5 ++++-
 test/camera/capture.cpp                            | 11 +++++++----
 test/camera/statemachine.cpp                       |  5 ++++-
 test/fence.cpp                                     |  6 +++++-
 test/mapped-buffer.cpp                             |  5 ++++-
 24 files changed, 90 insertions(+), 42 deletions(-)

Comments

Jacopo Mondi Dec. 16, 2022, 2:15 p.m. UTC | #1
Hi Paul

On Fri, Dec 16, 2022 at 09:29:24PM +0900, Paul Elder via libcamera-devel wrote:
> From: Nícolas F. R. A. Prado <nfraprado@collabora.com>
>
> Make FrameBufferAllocator::allocate() require a 'count' argument for the
> number of buffers to be allocated.
>
> Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
>

Seeing this being extensively reviewed, I won't comment on the change
to the public API interface. I'm a bit afraid we're putting on
applications the burden of getting this right, when they probably
don't care in most cases..

> ---

[snip]

>  /**
> diff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp
> index dabd9219..6a0bb8df 100644
> --- a/src/libcamera/framebuffer_allocator.cpp
> +++ b/src/libcamera/framebuffer_allocator.cpp
> @@ -71,6 +71,7 @@ FrameBufferAllocator::~FrameBufferAllocator()
>  /**
>   * \brief Allocate buffers for a configured stream
>   * \param[in] stream The stream to allocate buffers for
> + * \param[in] count The number of buffers to allocate
>   *
>   * Allocate buffers suitable for capturing frames from the \a stream. The Camera
>   * shall have been previously configured with Camera::configure() and shall be
> @@ -79,6 +80,10 @@ FrameBufferAllocator::~FrameBufferAllocator()
>   * Upon successful allocation, the allocated buffers can be retrieved with the
>   * buffers() function.
>   *
> + * This function may allocate less buffers than requested, due to memory and
> + * other system constraints. The caller shall always check the return value to
> + * verify if the number of allocate buffers matches its needs.
> + *
>   * \return The number of allocated buffers on success or a negative error code
>   * otherwise
>   * \retval -EACCES The camera is not in a state where buffers can be allocated
> @@ -86,7 +91,7 @@ FrameBufferAllocator::~FrameBufferAllocator()
>   * not part of the active camera configuration
>   * \retval -EBUSY Buffers are already allocated for the \a stream
>   */
> -int FrameBufferAllocator::allocate(Stream *stream)
> +int FrameBufferAllocator::allocate(Stream *stream, unsigned int count)
>  {
>  	const auto &[it, inserted] = buffers_.try_emplace(stream);
>
> @@ -95,7 +100,7 @@ int FrameBufferAllocator::allocate(Stream *stream)
>  		return -EBUSY;
>  	}
>
> -	int ret = camera_->exportFrameBuffers(stream, &it->second);
> +	int ret = camera_->exportFrameBuffers(stream, count, &it->second);

We have the camera, we can access properties, count could be

        count = max(properties::MinimumRequests, count)

A detail through

Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>


>  	if (ret == -EINVAL)
>  		LOG(Allocator, Error)
>  			<< "Stream is not part of " << camera_->id()
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index 98a4a3e5..bab2db65 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -140,7 +140,7 @@ public:
>  		const StreamRoles &roles) override;
>  	int configure(Camera *camera, CameraConfiguration *config) override;
>
> -	int exportFrameBuffers(Camera *camera, Stream *stream,
> +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
>  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>
>  	int start(Camera *camera, const ControlList *controls) override;
> @@ -687,10 +687,10 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
>  }
>
>  int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,
> +					    unsigned int count,
>  					    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>  	IPU3CameraData *data = cameraData(camera);
> -	unsigned int count = stream->configuration().bufferCount;
>
>  	if (stream == &data->outStream_)
>  		return data->imgu_->output_->exportBuffers(count, buffers);
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index 4a08d01e..4641c76f 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -328,7 +328,7 @@ public:
>  		const StreamRoles &roles) override;
>  	int configure(Camera *camera, CameraConfiguration *config) override;
>
> -	int exportFrameBuffers(Camera *camera, Stream *stream,
> +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
>  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>
>  	int start(Camera *camera, const ControlList *controls) override;
> @@ -1005,10 +1005,10 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
>  }
>
>  int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
> +					   unsigned int count,
>  					   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>  	RPi::Stream *s = static_cast<RPi::Stream *>(stream);
> -	unsigned int count = stream->configuration().bufferCount;
>  	int ret = s->dev()->exportBuffers(count, buffers);
>
>  	s->setExportedBuffers(buffers);
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 45b6beaf..8fe37c4f 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -148,7 +148,7 @@ public:
>  		const StreamRoles &roles) override;
>  	int configure(Camera *camera, CameraConfiguration *config) override;
>
> -	int exportFrameBuffers(Camera *camera, Stream *stream,
> +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
>  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>
>  	int start(Camera *camera, const ControlList *controls) override;
> @@ -816,10 +816,10 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
>  }
>
>  int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
> +					      unsigned int count,
>  					      std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>  	RkISP1CameraData *data = cameraData(camera);
> -	unsigned int count = stream->configuration().bufferCount;
>
>  	if (stream == &data->mainPathStream_)
>  		return mainPath_.exportBuffers(count, buffers);
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 8ed983fe..6b7c6d5c 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -322,7 +322,7 @@ public:
>  		const StreamRoles &roles) override;
>  	int configure(Camera *camera, CameraConfiguration *config) override;
>
> -	int exportFrameBuffers(Camera *camera, Stream *stream,
> +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
>  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>
>  	int start(Camera *camera, const ControlList *controls) override;
> @@ -1202,10 +1202,10 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
>  }
>
>  int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
> +					      unsigned int count,
>  					      std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>  	SimpleCameraData *data = cameraData(camera);
> -	unsigned int count = stream->configuration().bufferCount;
>
>  	/*
>  	 * Export buffers on the converter or capture video node, depending on
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> index 7f580955..4ce240a4 100644
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> @@ -78,7 +78,7 @@ public:
>  		const StreamRoles &roles) override;
>  	int configure(Camera *camera, CameraConfiguration *config) override;
>
> -	int exportFrameBuffers(Camera *camera, Stream *stream,
> +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
>  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>
>  	int start(Camera *camera, const ControlList *controls) override;
> @@ -226,11 +226,12 @@ int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config)
>  	return 0;
>  }
>
> -int PipelineHandlerUVC::exportFrameBuffers(Camera *camera, Stream *stream,
> +int PipelineHandlerUVC::exportFrameBuffers(Camera *camera,
> +					   [[maybe_unused]] Stream *stream,
> +					   unsigned int count,
>  					   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>  	UVCCameraData *data = cameraData(camera);
> -	unsigned int count = stream->configuration().bufferCount;
>
>  	return data->video_->exportBuffers(count, buffers);
>  }
> diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> index d2633be4..e58caf99 100644
> --- a/src/libcamera/pipeline/vimc/vimc.cpp
> +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> @@ -89,7 +89,7 @@ public:
>  		const StreamRoles &roles) override;
>  	int configure(Camera *camera, CameraConfiguration *config) override;
>
> -	int exportFrameBuffers(Camera *camera, Stream *stream,
> +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
>  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>
>  	int start(Camera *camera, const ControlList *controls) override;
> @@ -321,11 +321,12 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
>  	return 0;
>  }
>
> -int PipelineHandlerVimc::exportFrameBuffers(Camera *camera, Stream *stream,
> +int PipelineHandlerVimc::exportFrameBuffers(Camera *camera,
> +					    [[maybe_unused]] Stream *stream,
> +					    unsigned int count,
>  					    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
>  {
>  	VimcCameraData *data = cameraData(camera);
> -	unsigned int count = stream->configuration().bufferCount;
>
>  	return data->video_->exportBuffers(count, buffers);
>  }
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index cfade490..fffbd51b 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -280,6 +280,7 @@ void PipelineHandler::unlockMediaDevices()
>   * \brief Allocate and export buffers for \a stream
>   * \param[in] camera The camera
>   * \param[in] stream The stream to allocate buffers for
> + * \param[in] count The number of buffers to allocate
>   * \param[out] buffers Array of buffers successfully allocated
>   *
>   * This function allocates buffers for the \a stream from the devices associated
> diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> index 7b97c2d5..1c5fab64 100644
> --- a/src/v4l2/v4l2_camera.cpp
> +++ b/src/v4l2/v4l2_camera.cpp
> @@ -160,7 +160,7 @@ int V4L2Camera::allocBuffers(unsigned int count)
>  {
>  	Stream *stream = config_->at(0).stream();
>
> -	int ret = bufferAllocator_->allocate(stream);
> +	int ret = bufferAllocator_->allocate(stream, count);
>  	if (ret < 0)
>  		return ret;
>
> diff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp
> index 06c87730..63a97863 100644
> --- a/test/camera/camera_reconfigure.cpp
> +++ b/test/camera/camera_reconfigure.cpp
> @@ -17,6 +17,7 @@
>  #include <libcamera/base/timer.h>
>
>  #include <libcamera/framebuffer_allocator.h>
> +#include <libcamera/property_ids.h>
>
>  #include "camera_test.h"
>  #include "test.h"
> @@ -78,7 +79,9 @@ private:
>  		 * same buffer allocation for each run.
>  		 */
>  		if (!allocated_) {
> -			int ret = allocator_->allocate(stream);
> +			unsigned int bufferCount =
> +				camera_->properties().get(properties::MinimumRequests).value();
> +			int ret = allocator_->allocate(stream, bufferCount);
>  			if (ret < 0) {
>  				cerr << "Failed to allocate buffers" << endl;
>  				return TestFail;
> diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp
> index de824083..64d3a8e7 100644
> --- a/test/camera/capture.cpp
> +++ b/test/camera/capture.cpp
> @@ -7,12 +7,13 @@
>
>  #include <iostream>
>
> -#include <libcamera/framebuffer_allocator.h>
> -
>  #include <libcamera/base/event_dispatcher.h>
>  #include <libcamera/base/thread.h>
>  #include <libcamera/base/timer.h>
>
> +#include <libcamera/framebuffer_allocator.h>
> +#include <libcamera/property_ids.h>
> +
>  #include "camera_test.h"
>  #include "test.h"
>
> @@ -98,8 +99,10 @@ protected:
>
>  		Stream *stream = cfg.stream();
>
> -		int ret = allocator_->allocate(stream);
> -		if (ret < 0)
> +		unsigned int bufferCount =
> +			camera_->properties().get(properties::MinimumRequests).value();
> +		int ret = allocator_->allocate(stream, bufferCount);
> +		if (ret < static_cast<int>(bufferCount))
>  			return TestFail;
>
>  		for (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
> diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp
> index 9c2b0c6a..a9ddb323 100644
> --- a/test/camera/statemachine.cpp
> +++ b/test/camera/statemachine.cpp
> @@ -8,6 +8,7 @@
>  #include <iostream>
>
>  #include <libcamera/framebuffer_allocator.h>
> +#include <libcamera/property_ids.h>
>
>  #include "camera_test.h"
>  #include "test.h"
> @@ -120,7 +121,9 @@ protected:
>  		/* Use internally allocated buffers. */
>  		allocator_ = new FrameBufferAllocator(camera_);
>  		Stream *stream = *camera_->streams().begin();
> -		if (allocator_->allocate(stream) < 0)
> +		unsigned int bufferCount =
> +			camera_->properties().get(properties::MinimumRequests).value();
> +		if (allocator_->allocate(stream, bufferCount) < 0)
>  			return TestFail;
>
>  		if (camera_->start())
> diff --git a/test/fence.cpp b/test/fence.cpp
> index 1e38bc2f..88ce2857 100644
> --- a/test/fence.cpp
> +++ b/test/fence.cpp
> @@ -18,6 +18,7 @@
>
>  #include <libcamera/fence.h>
>  #include <libcamera/framebuffer_allocator.h>
> +#include <libcamera/property_ids.h>
>
>  #include "camera_test.h"
>  #include "test.h"
> @@ -117,8 +118,11 @@ int FenceTest::init()
>  	StreamConfiguration &cfg = config_->at(0);
>  	stream_ = cfg.stream();
>
> +	unsigned int bufferCount =
> +		camera_->properties().get(properties::MinimumRequests).value();
> +
>  	allocator_ = std::make_unique<FrameBufferAllocator>(camera_);
> -	if (allocator_->allocate(stream_) < 0)
> +	if (allocator_->allocate(stream_, bufferCount) < 0)
>  		return TestFail;
>
>  	nbuffers_ = allocator_->buffers(stream_).size();
> diff --git a/test/mapped-buffer.cpp b/test/mapped-buffer.cpp
> index b4422f7d..1b98feea 100644
> --- a/test/mapped-buffer.cpp
> +++ b/test/mapped-buffer.cpp
> @@ -8,6 +8,7 @@
>  #include <iostream>
>
>  #include <libcamera/framebuffer_allocator.h>
> +#include <libcamera/property_ids.h>
>
>  #include "libcamera/internal/mapped_framebuffer.h"
>
> @@ -55,7 +56,9 @@ protected:
>
>  		stream_ = cfg.stream();
>
> -		int ret = allocator_->allocate(stream_);
> +		unsigned int bufferCount =
> +			camera_->properties().get(properties::MinimumRequests).value();
> +		int ret = allocator_->allocate(stream_, bufferCount);
>  		if (ret < 0)
>  			return TestFail;
>
> --
> 2.35.1
>
Paul Elder Dec. 21, 2022, 8:10 a.m. UTC | #2
On Fri, Dec 16, 2022 at 03:15:35PM +0100, Jacopo Mondi wrote:
> Hi Paul
> 
> On Fri, Dec 16, 2022 at 09:29:24PM +0900, Paul Elder via libcamera-devel wrote:
> > From: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> >
> > Make FrameBufferAllocator::allocate() require a 'count' argument for the
> > number of buffers to be allocated.
> >
> > Signed-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> >
> 
> Seeing this being extensively reviewed, I won't comment on the change
> to the public API interface. I'm a bit afraid we're putting on
> applications the burden of getting this right, when they probably
> don't care in most cases..

(I went back through the history) It looks like it was decided in v2 to
go this way.


Paul

> 
> > ---
> 
> [snip]
> 
> >  /**
> > diff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp
> > index dabd9219..6a0bb8df 100644
> > --- a/src/libcamera/framebuffer_allocator.cpp
> > +++ b/src/libcamera/framebuffer_allocator.cpp
> > @@ -71,6 +71,7 @@ FrameBufferAllocator::~FrameBufferAllocator()
> >  /**
> >   * \brief Allocate buffers for a configured stream
> >   * \param[in] stream The stream to allocate buffers for
> > + * \param[in] count The number of buffers to allocate
> >   *
> >   * Allocate buffers suitable for capturing frames from the \a stream. The Camera
> >   * shall have been previously configured with Camera::configure() and shall be
> > @@ -79,6 +80,10 @@ FrameBufferAllocator::~FrameBufferAllocator()
> >   * Upon successful allocation, the allocated buffers can be retrieved with the
> >   * buffers() function.
> >   *
> > + * This function may allocate less buffers than requested, due to memory and
> > + * other system constraints. The caller shall always check the return value to
> > + * verify if the number of allocate buffers matches its needs.
> > + *
> >   * \return The number of allocated buffers on success or a negative error code
> >   * otherwise
> >   * \retval -EACCES The camera is not in a state where buffers can be allocated
> > @@ -86,7 +91,7 @@ FrameBufferAllocator::~FrameBufferAllocator()
> >   * not part of the active camera configuration
> >   * \retval -EBUSY Buffers are already allocated for the \a stream
> >   */
> > -int FrameBufferAllocator::allocate(Stream *stream)
> > +int FrameBufferAllocator::allocate(Stream *stream, unsigned int count)
> >  {
> >  	const auto &[it, inserted] = buffers_.try_emplace(stream);
> >
> > @@ -95,7 +100,7 @@ int FrameBufferAllocator::allocate(Stream *stream)
> >  		return -EBUSY;
> >  	}
> >
> > -	int ret = camera_->exportFrameBuffers(stream, &it->second);
> > +	int ret = camera_->exportFrameBuffers(stream, count, &it->second);
> 
> We have the camera, we can access properties, count could be
> 
>         count = max(properties::MinimumRequests, count)
> 
> A detail through
> 
> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
> 
> 
> >  	if (ret == -EINVAL)
> >  		LOG(Allocator, Error)
> >  			<< "Stream is not part of " << camera_->id()
> > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > index 98a4a3e5..bab2db65 100644
> > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > @@ -140,7 +140,7 @@ public:
> >  		const StreamRoles &roles) override;
> >  	int configure(Camera *camera, CameraConfiguration *config) override;
> >
> > -	int exportFrameBuffers(Camera *camera, Stream *stream,
> > +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
> >  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >  	int start(Camera *camera, const ControlList *controls) override;
> > @@ -687,10 +687,10 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
> >  }
> >
> >  int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,
> > +					    unsigned int count,
> >  					    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> >  {
> >  	IPU3CameraData *data = cameraData(camera);
> > -	unsigned int count = stream->configuration().bufferCount;
> >
> >  	if (stream == &data->outStream_)
> >  		return data->imgu_->output_->exportBuffers(count, buffers);
> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > index 4a08d01e..4641c76f 100644
> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > @@ -328,7 +328,7 @@ public:
> >  		const StreamRoles &roles) override;
> >  	int configure(Camera *camera, CameraConfiguration *config) override;
> >
> > -	int exportFrameBuffers(Camera *camera, Stream *stream,
> > +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
> >  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >  	int start(Camera *camera, const ControlList *controls) override;
> > @@ -1005,10 +1005,10 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
> >  }
> >
> >  int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
> > +					   unsigned int count,
> >  					   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> >  {
> >  	RPi::Stream *s = static_cast<RPi::Stream *>(stream);
> > -	unsigned int count = stream->configuration().bufferCount;
> >  	int ret = s->dev()->exportBuffers(count, buffers);
> >
> >  	s->setExportedBuffers(buffers);
> > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > index 45b6beaf..8fe37c4f 100644
> > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > @@ -148,7 +148,7 @@ public:
> >  		const StreamRoles &roles) override;
> >  	int configure(Camera *camera, CameraConfiguration *config) override;
> >
> > -	int exportFrameBuffers(Camera *camera, Stream *stream,
> > +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
> >  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >  	int start(Camera *camera, const ControlList *controls) override;
> > @@ -816,10 +816,10 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
> >  }
> >
> >  int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
> > +					      unsigned int count,
> >  					      std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> >  {
> >  	RkISP1CameraData *data = cameraData(camera);
> > -	unsigned int count = stream->configuration().bufferCount;
> >
> >  	if (stream == &data->mainPathStream_)
> >  		return mainPath_.exportBuffers(count, buffers);
> > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> > index 8ed983fe..6b7c6d5c 100644
> > --- a/src/libcamera/pipeline/simple/simple.cpp
> > +++ b/src/libcamera/pipeline/simple/simple.cpp
> > @@ -322,7 +322,7 @@ public:
> >  		const StreamRoles &roles) override;
> >  	int configure(Camera *camera, CameraConfiguration *config) override;
> >
> > -	int exportFrameBuffers(Camera *camera, Stream *stream,
> > +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
> >  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >  	int start(Camera *camera, const ControlList *controls) override;
> > @@ -1202,10 +1202,10 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
> >  }
> >
> >  int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
> > +					      unsigned int count,
> >  					      std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> >  {
> >  	SimpleCameraData *data = cameraData(camera);
> > -	unsigned int count = stream->configuration().bufferCount;
> >
> >  	/*
> >  	 * Export buffers on the converter or capture video node, depending on
> > diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > index 7f580955..4ce240a4 100644
> > --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > @@ -78,7 +78,7 @@ public:
> >  		const StreamRoles &roles) override;
> >  	int configure(Camera *camera, CameraConfiguration *config) override;
> >
> > -	int exportFrameBuffers(Camera *camera, Stream *stream,
> > +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
> >  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >  	int start(Camera *camera, const ControlList *controls) override;
> > @@ -226,11 +226,12 @@ int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config)
> >  	return 0;
> >  }
> >
> > -int PipelineHandlerUVC::exportFrameBuffers(Camera *camera, Stream *stream,
> > +int PipelineHandlerUVC::exportFrameBuffers(Camera *camera,
> > +					   [[maybe_unused]] Stream *stream,
> > +					   unsigned int count,
> >  					   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> >  {
> >  	UVCCameraData *data = cameraData(camera);
> > -	unsigned int count = stream->configuration().bufferCount;
> >
> >  	return data->video_->exportBuffers(count, buffers);
> >  }
> > diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> > index d2633be4..e58caf99 100644
> > --- a/src/libcamera/pipeline/vimc/vimc.cpp
> > +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> > @@ -89,7 +89,7 @@ public:
> >  		const StreamRoles &roles) override;
> >  	int configure(Camera *camera, CameraConfiguration *config) override;
> >
> > -	int exportFrameBuffers(Camera *camera, Stream *stream,
> > +	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
> >  			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >  	int start(Camera *camera, const ControlList *controls) override;
> > @@ -321,11 +321,12 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
> >  	return 0;
> >  }
> >
> > -int PipelineHandlerVimc::exportFrameBuffers(Camera *camera, Stream *stream,
> > +int PipelineHandlerVimc::exportFrameBuffers(Camera *camera,
> > +					    [[maybe_unused]] Stream *stream,
> > +					    unsigned int count,
> >  					    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
> >  {
> >  	VimcCameraData *data = cameraData(camera);
> > -	unsigned int count = stream->configuration().bufferCount;
> >
> >  	return data->video_->exportBuffers(count, buffers);
> >  }
> > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > index cfade490..fffbd51b 100644
> > --- a/src/libcamera/pipeline_handler.cpp
> > +++ b/src/libcamera/pipeline_handler.cpp
> > @@ -280,6 +280,7 @@ void PipelineHandler::unlockMediaDevices()
> >   * \brief Allocate and export buffers for \a stream
> >   * \param[in] camera The camera
> >   * \param[in] stream The stream to allocate buffers for
> > + * \param[in] count The number of buffers to allocate
> >   * \param[out] buffers Array of buffers successfully allocated
> >   *
> >   * This function allocates buffers for the \a stream from the devices associated
> > diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> > index 7b97c2d5..1c5fab64 100644
> > --- a/src/v4l2/v4l2_camera.cpp
> > +++ b/src/v4l2/v4l2_camera.cpp
> > @@ -160,7 +160,7 @@ int V4L2Camera::allocBuffers(unsigned int count)
> >  {
> >  	Stream *stream = config_->at(0).stream();
> >
> > -	int ret = bufferAllocator_->allocate(stream);
> > +	int ret = bufferAllocator_->allocate(stream, count);
> >  	if (ret < 0)
> >  		return ret;
> >
> > diff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp
> > index 06c87730..63a97863 100644
> > --- a/test/camera/camera_reconfigure.cpp
> > +++ b/test/camera/camera_reconfigure.cpp
> > @@ -17,6 +17,7 @@
> >  #include <libcamera/base/timer.h>
> >
> >  #include <libcamera/framebuffer_allocator.h>
> > +#include <libcamera/property_ids.h>
> >
> >  #include "camera_test.h"
> >  #include "test.h"
> > @@ -78,7 +79,9 @@ private:
> >  		 * same buffer allocation for each run.
> >  		 */
> >  		if (!allocated_) {
> > -			int ret = allocator_->allocate(stream);
> > +			unsigned int bufferCount =
> > +				camera_->properties().get(properties::MinimumRequests).value();
> > +			int ret = allocator_->allocate(stream, bufferCount);
> >  			if (ret < 0) {
> >  				cerr << "Failed to allocate buffers" << endl;
> >  				return TestFail;
> > diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp
> > index de824083..64d3a8e7 100644
> > --- a/test/camera/capture.cpp
> > +++ b/test/camera/capture.cpp
> > @@ -7,12 +7,13 @@
> >
> >  #include <iostream>
> >
> > -#include <libcamera/framebuffer_allocator.h>
> > -
> >  #include <libcamera/base/event_dispatcher.h>
> >  #include <libcamera/base/thread.h>
> >  #include <libcamera/base/timer.h>
> >
> > +#include <libcamera/framebuffer_allocator.h>
> > +#include <libcamera/property_ids.h>
> > +
> >  #include "camera_test.h"
> >  #include "test.h"
> >
> > @@ -98,8 +99,10 @@ protected:
> >
> >  		Stream *stream = cfg.stream();
> >
> > -		int ret = allocator_->allocate(stream);
> > -		if (ret < 0)
> > +		unsigned int bufferCount =
> > +			camera_->properties().get(properties::MinimumRequests).value();
> > +		int ret = allocator_->allocate(stream, bufferCount);
> > +		if (ret < static_cast<int>(bufferCount))
> >  			return TestFail;
> >
> >  		for (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
> > diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp
> > index 9c2b0c6a..a9ddb323 100644
> > --- a/test/camera/statemachine.cpp
> > +++ b/test/camera/statemachine.cpp
> > @@ -8,6 +8,7 @@
> >  #include <iostream>
> >
> >  #include <libcamera/framebuffer_allocator.h>
> > +#include <libcamera/property_ids.h>
> >
> >  #include "camera_test.h"
> >  #include "test.h"
> > @@ -120,7 +121,9 @@ protected:
> >  		/* Use internally allocated buffers. */
> >  		allocator_ = new FrameBufferAllocator(camera_);
> >  		Stream *stream = *camera_->streams().begin();
> > -		if (allocator_->allocate(stream) < 0)
> > +		unsigned int bufferCount =
> > +			camera_->properties().get(properties::MinimumRequests).value();
> > +		if (allocator_->allocate(stream, bufferCount) < 0)
> >  			return TestFail;
> >
> >  		if (camera_->start())
> > diff --git a/test/fence.cpp b/test/fence.cpp
> > index 1e38bc2f..88ce2857 100644
> > --- a/test/fence.cpp
> > +++ b/test/fence.cpp
> > @@ -18,6 +18,7 @@
> >
> >  #include <libcamera/fence.h>
> >  #include <libcamera/framebuffer_allocator.h>
> > +#include <libcamera/property_ids.h>
> >
> >  #include "camera_test.h"
> >  #include "test.h"
> > @@ -117,8 +118,11 @@ int FenceTest::init()
> >  	StreamConfiguration &cfg = config_->at(0);
> >  	stream_ = cfg.stream();
> >
> > +	unsigned int bufferCount =
> > +		camera_->properties().get(properties::MinimumRequests).value();
> > +
> >  	allocator_ = std::make_unique<FrameBufferAllocator>(camera_);
> > -	if (allocator_->allocate(stream_) < 0)
> > +	if (allocator_->allocate(stream_, bufferCount) < 0)
> >  		return TestFail;
> >
> >  	nbuffers_ = allocator_->buffers(stream_).size();
> > diff --git a/test/mapped-buffer.cpp b/test/mapped-buffer.cpp
> > index b4422f7d..1b98feea 100644
> > --- a/test/mapped-buffer.cpp
> > +++ b/test/mapped-buffer.cpp
> > @@ -8,6 +8,7 @@
> >  #include <iostream>
> >
> >  #include <libcamera/framebuffer_allocator.h>
> > +#include <libcamera/property_ids.h>
> >
> >  #include "libcamera/internal/mapped_framebuffer.h"
> >
> > @@ -55,7 +56,9 @@ protected:
> >
> >  		stream_ = cfg.stream();
> >
> > -		int ret = allocator_->allocate(stream_);
> > +		unsigned int bufferCount =
> > +			camera_->properties().get(properties::MinimumRequests).value();
> > +		int ret = allocator_->allocate(stream_, bufferCount);
> >  		if (ret < 0)
> >  			return TestFail;
> >
> > --
> > 2.35.1
> >

Patch
diff mbox series

diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst
index 1b2d7727..40bcf67a 100644
--- a/Documentation/guides/application-developer.rst
+++ b/Documentation/guides/application-developer.rst
@@ -30,6 +30,7 @@  defined names and types without the need of prefixing them.
    #include <thread>
 
    #include <libcamera/libcamera.h>
+   #include <libcamera/property_ids.h>
 
    using namespace libcamera;
    using namespace std::chrono_literals;
@@ -272,7 +273,10 @@  Using the libcamera ``FrameBufferAllocator``
 
 Applications create a ``FrameBufferAllocator`` for a Camera and use it
 to allocate buffers for streams of a ``CameraConfiguration`` with the
-``allocate()`` function.
+``allocate()`` function. The number of buffers to be allocated needs to be
+specified, and should be at least equal to the value of the ``MinimumRequests``
+property in order for the pipeline to have enough requests to be able to
+capture without frame drops.
 
 The list of allocated buffers can be retrieved using the ``Stream`` instance
 as the parameter of the ``FrameBufferAllocator::buffers()`` function.
@@ -280,9 +284,10 @@  as the parameter of the ``FrameBufferAllocator::buffers()`` function.
 .. code:: cpp
 
    FrameBufferAllocator *allocator = new FrameBufferAllocator(camera);
+   unsigned int bufferCount = camera->properties().get(properties::MinimumRequests);
 
    for (StreamConfiguration &cfg : *config) {
-       int ret = allocator->allocate(cfg.stream());
+       int ret = allocator->allocate(cfg.stream(), bufferCount);
        if (ret < 0) {
            std::cerr << "Can't allocate buffers" << std::endl;
            return -ENOMEM;
diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst
index a7356e4a..70de82af 100644
--- a/Documentation/guides/pipeline-handler.rst
+++ b/Documentation/guides/pipeline-handler.rst
@@ -206,7 +206,7 @@  implementations for the overridden class members.
           const StreamRoles &roles) override;
           int configure(Camera *camera, CameraConfiguration *config) override;
 
-          int exportFrameBuffers(Camera *camera, Stream *stream,
+          int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
           std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
           int start(Camera *camera, const ControlList *controls) override;
@@ -1187,7 +1187,6 @@  handle this:
 
 .. code-block:: cpp
 
-   unsigned int count = stream->configuration().bufferCount;
    VividCameraData *data = cameraData(camera);
 
    return data->video_->exportBuffers(count, buffers);
diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h
index 5bb06584..4d32d16b 100644
--- a/include/libcamera/camera.h
+++ b/include/libcamera/camera.h
@@ -126,7 +126,7 @@  private:
 	void requestComplete(Request *request);
 
 	friend class FrameBufferAllocator;
-	int exportFrameBuffers(Stream *stream,
+	int exportFrameBuffers(Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers);
 };
 
diff --git a/include/libcamera/framebuffer_allocator.h b/include/libcamera/framebuffer_allocator.h
index 45ff232b..7a0bf1a0 100644
--- a/include/libcamera/framebuffer_allocator.h
+++ b/include/libcamera/framebuffer_allocator.h
@@ -25,7 +25,7 @@  public:
 	FrameBufferAllocator(std::shared_ptr<Camera> camera);
 	~FrameBufferAllocator();
 
-	int allocate(Stream *stream);
+	int allocate(Stream *stream, unsigned int count);
 	int free(Stream *stream);
 
 	bool allocated() const { return !buffers_.empty(); }
diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index ec4f662d..8af165a3 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -52,7 +52,7 @@  public:
 		const StreamRoles &roles) = 0;
 	virtual int configure(Camera *camera, CameraConfiguration *config) = 0;
 
-	virtual int exportFrameBuffers(Camera *camera, Stream *stream,
+	virtual int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 				       std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
 
 	virtual int start(Camera *camera, const ControlList *controls) = 0;
diff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp
index 8fcec630..b2f64e39 100644
--- a/src/apps/cam/camera_session.cpp
+++ b/src/apps/cam/camera_session.cpp
@@ -253,17 +253,22 @@  int CameraSession::startCapture()
 {
 	int ret;
 
-	/* Identify the stream with the least number of buffers. */
-	unsigned int nbuffers = UINT_MAX;
+	unsigned int nbuffers =
+		camera_->properties().get(properties::MinimumRequests).value();
+
 	for (StreamConfiguration &cfg : *config_) {
-		ret = allocator_->allocate(cfg.stream());
+		ret = allocator_->allocate(cfg.stream(), nbuffers);
 		if (ret < 0) {
 			std::cerr << "Can't allocate buffers" << std::endl;
 			return -ENOMEM;
 		}
 
 		unsigned int allocated = allocator_->buffers(cfg.stream()).size();
-		nbuffers = std::min(nbuffers, allocated);
+		if (allocated < nbuffers) {
+			std::cerr << "Unable to allocate enough buffers"
+				  << std::endl;
+			return -ENOMEM;
+		}
 	}
 
 	/*
diff --git a/src/apps/lc-compliance/simple_capture.cpp b/src/apps/lc-compliance/simple_capture.cpp
index cf4d7cf3..be495986 100644
--- a/src/apps/lc-compliance/simple_capture.cpp
+++ b/src/apps/lc-compliance/simple_capture.cpp
@@ -7,6 +7,8 @@ 
 
 #include <gtest/gtest.h>
 
+#include <libcamera/property_ids.h>
+
 #include "simple_capture.h"
 
 using namespace libcamera;
@@ -44,11 +46,13 @@  void SimpleCapture::configure(StreamRole role)
 
 void SimpleCapture::start()
 {
+	unsigned int bufferCount =
+		camera_->properties().get(properties::MinimumRequests).value();
 	Stream *stream = config_->at(0).stream();
-	int count = allocator_->allocate(stream);
+	int count = allocator_->allocate(stream, bufferCount);
 
 	ASSERT_GE(count, 0) << "Failed to allocate buffers";
-	EXPECT_EQ(count, config_->at(0).bufferCount) << "Allocated less buffers than expected";
+	EXPECT_EQ(count, bufferCount) << "Allocated less buffers than expected";
 
 	camera_->requestCompleted.connect(this, &SimpleCapture::requestComplete);
 
diff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp
index fb2db4aa..85b696fe 100644
--- a/src/apps/qcam/main_window.cpp
+++ b/src/apps/qcam/main_window.cpp
@@ -12,6 +12,7 @@ 
 #include <string>
 
 #include <libcamera/camera_manager.h>
+#include <libcamera/property_ids.h>
 #include <libcamera/version.h>
 
 #include <QCoreApplication>
@@ -464,7 +465,14 @@  int MainWindow::startCapture()
 	for (StreamConfiguration &config : *config_) {
 		Stream *stream = config.stream();
 
-		ret = allocator_->allocate(stream);
+		/*
+		 * We hold on to a buffer for display, so need one extra from
+		 * the minimum required for capture.
+		 */
+		unsigned int bufferCount =
+			camera_->properties().get(properties::MinimumRequests).value() + 1;
+
+		ret = allocator_->allocate(stream, bufferCount);
 		if (ret < 0) {
 			qWarning() << "Failed to allocate capture buffers";
 			goto error;
diff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp
index c740b8fc..5f9b96ad 100644
--- a/src/gstreamer/gstlibcameraallocator.cpp
+++ b/src/gstreamer/gstlibcameraallocator.cpp
@@ -10,6 +10,7 @@ 
 
 #include <libcamera/camera.h>
 #include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
 #include <libcamera/stream.h>
 
 #include "gstlibcamera-utils.h"
@@ -193,13 +194,15 @@  gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,
 {
 	auto *self = GST_LIBCAMERA_ALLOCATOR(g_object_new(GST_TYPE_LIBCAMERA_ALLOCATOR,
 							  nullptr));
+	unsigned int bufferCount =
+		camera->properties().get(properties::MinimumRequests).value();
 
 	self->fb_allocator = new FrameBufferAllocator(camera);
 	for (StreamConfiguration &streamCfg : *config_) {
 		Stream *stream = streamCfg.stream();
 		gint ret;
 
-		ret = self->fb_allocator->allocate(stream);
+		ret = self->fb_allocator->allocate(stream, bufferCount);
 		if (ret == 0)
 			return nullptr;
 
diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp
index 2d947a44..ad289b28 100644
--- a/src/libcamera/camera.cpp
+++ b/src/libcamera/camera.cpp
@@ -779,7 +779,7 @@  void Camera::disconnect()
 	disconnected.emit();
 }
 
-int Camera::exportFrameBuffers(Stream *stream,
+int Camera::exportFrameBuffers(Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	Private *const d = _d();
@@ -796,7 +796,7 @@  int Camera::exportFrameBuffers(Stream *stream,
 
 	return d->pipe_->invokeMethod(&PipelineHandler::exportFrameBuffers,
 				      ConnectionTypeBlocking, this, stream,
-				      buffers);
+				      count, buffers);
 }
 
 /**
diff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp
index dabd9219..6a0bb8df 100644
--- a/src/libcamera/framebuffer_allocator.cpp
+++ b/src/libcamera/framebuffer_allocator.cpp
@@ -71,6 +71,7 @@  FrameBufferAllocator::~FrameBufferAllocator()
 /**
  * \brief Allocate buffers for a configured stream
  * \param[in] stream The stream to allocate buffers for
+ * \param[in] count The number of buffers to allocate
  *
  * Allocate buffers suitable for capturing frames from the \a stream. The Camera
  * shall have been previously configured with Camera::configure() and shall be
@@ -79,6 +80,10 @@  FrameBufferAllocator::~FrameBufferAllocator()
  * Upon successful allocation, the allocated buffers can be retrieved with the
  * buffers() function.
  *
+ * This function may allocate less buffers than requested, due to memory and
+ * other system constraints. The caller shall always check the return value to
+ * verify if the number of allocate buffers matches its needs.
+ *
  * \return The number of allocated buffers on success or a negative error code
  * otherwise
  * \retval -EACCES The camera is not in a state where buffers can be allocated
@@ -86,7 +91,7 @@  FrameBufferAllocator::~FrameBufferAllocator()
  * not part of the active camera configuration
  * \retval -EBUSY Buffers are already allocated for the \a stream
  */
-int FrameBufferAllocator::allocate(Stream *stream)
+int FrameBufferAllocator::allocate(Stream *stream, unsigned int count)
 {
 	const auto &[it, inserted] = buffers_.try_emplace(stream);
 
@@ -95,7 +100,7 @@  int FrameBufferAllocator::allocate(Stream *stream)
 		return -EBUSY;
 	}
 
-	int ret = camera_->exportFrameBuffers(stream, &it->second);
+	int ret = camera_->exportFrameBuffers(stream, count, &it->second);
 	if (ret == -EINVAL)
 		LOG(Allocator, Error)
 			<< "Stream is not part of " << camera_->id()
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index 98a4a3e5..bab2db65 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -140,7 +140,7 @@  public:
 		const StreamRoles &roles) override;
 	int configure(Camera *camera, CameraConfiguration *config) override;
 
-	int exportFrameBuffers(Camera *camera, Stream *stream,
+	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
@@ -687,10 +687,10 @@  int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)
 }
 
 int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,
+					    unsigned int count,
 					    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	IPU3CameraData *data = cameraData(camera);
-	unsigned int count = stream->configuration().bufferCount;
 
 	if (stream == &data->outStream_)
 		return data->imgu_->output_->exportBuffers(count, buffers);
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index 4a08d01e..4641c76f 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -328,7 +328,7 @@  public:
 		const StreamRoles &roles) override;
 	int configure(Camera *camera, CameraConfiguration *config) override;
 
-	int exportFrameBuffers(Camera *camera, Stream *stream,
+	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
@@ -1005,10 +1005,10 @@  int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)
 }
 
 int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
+					   unsigned int count,
 					   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	RPi::Stream *s = static_cast<RPi::Stream *>(stream);
-	unsigned int count = stream->configuration().bufferCount;
 	int ret = s->dev()->exportBuffers(count, buffers);
 
 	s->setExportedBuffers(buffers);
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 45b6beaf..8fe37c4f 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -148,7 +148,7 @@  public:
 		const StreamRoles &roles) override;
 	int configure(Camera *camera, CameraConfiguration *config) override;
 
-	int exportFrameBuffers(Camera *camera, Stream *stream,
+	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
@@ -816,10 +816,10 @@  int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)
 }
 
 int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,
+					      unsigned int count,
 					      std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	RkISP1CameraData *data = cameraData(camera);
-	unsigned int count = stream->configuration().bufferCount;
 
 	if (stream == &data->mainPathStream_)
 		return mainPath_.exportBuffers(count, buffers);
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 8ed983fe..6b7c6d5c 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -322,7 +322,7 @@  public:
 		const StreamRoles &roles) override;
 	int configure(Camera *camera, CameraConfiguration *config) override;
 
-	int exportFrameBuffers(Camera *camera, Stream *stream,
+	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
@@ -1202,10 +1202,10 @@  int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
 }
 
 int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+					      unsigned int count,
 					      std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	SimpleCameraData *data = cameraData(camera);
-	unsigned int count = stream->configuration().bufferCount;
 
 	/*
 	 * Export buffers on the converter or capture video node, depending on
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 7f580955..4ce240a4 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -78,7 +78,7 @@  public:
 		const StreamRoles &roles) override;
 	int configure(Camera *camera, CameraConfiguration *config) override;
 
-	int exportFrameBuffers(Camera *camera, Stream *stream,
+	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
@@ -226,11 +226,12 @@  int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config)
 	return 0;
 }
 
-int PipelineHandlerUVC::exportFrameBuffers(Camera *camera, Stream *stream,
+int PipelineHandlerUVC::exportFrameBuffers(Camera *camera,
+					   [[maybe_unused]] Stream *stream,
+					   unsigned int count,
 					   std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	UVCCameraData *data = cameraData(camera);
-	unsigned int count = stream->configuration().bufferCount;
 
 	return data->video_->exportBuffers(count, buffers);
 }
diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
index d2633be4..e58caf99 100644
--- a/src/libcamera/pipeline/vimc/vimc.cpp
+++ b/src/libcamera/pipeline/vimc/vimc.cpp
@@ -89,7 +89,7 @@  public:
 		const StreamRoles &roles) override;
 	int configure(Camera *camera, CameraConfiguration *config) override;
 
-	int exportFrameBuffers(Camera *camera, Stream *stream,
+	int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
@@ -321,11 +321,12 @@  int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)
 	return 0;
 }
 
-int PipelineHandlerVimc::exportFrameBuffers(Camera *camera, Stream *stream,
+int PipelineHandlerVimc::exportFrameBuffers(Camera *camera,
+					    [[maybe_unused]] Stream *stream,
+					    unsigned int count,
 					    std::vector<std::unique_ptr<FrameBuffer>> *buffers)
 {
 	VimcCameraData *data = cameraData(camera);
-	unsigned int count = stream->configuration().bufferCount;
 
 	return data->video_->exportBuffers(count, buffers);
 }
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index cfade490..fffbd51b 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -280,6 +280,7 @@  void PipelineHandler::unlockMediaDevices()
  * \brief Allocate and export buffers for \a stream
  * \param[in] camera The camera
  * \param[in] stream The stream to allocate buffers for
+ * \param[in] count The number of buffers to allocate
  * \param[out] buffers Array of buffers successfully allocated
  *
  * This function allocates buffers for the \a stream from the devices associated
diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index 7b97c2d5..1c5fab64 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -160,7 +160,7 @@  int V4L2Camera::allocBuffers(unsigned int count)
 {
 	Stream *stream = config_->at(0).stream();
 
-	int ret = bufferAllocator_->allocate(stream);
+	int ret = bufferAllocator_->allocate(stream, count);
 	if (ret < 0)
 		return ret;
 
diff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp
index 06c87730..63a97863 100644
--- a/test/camera/camera_reconfigure.cpp
+++ b/test/camera/camera_reconfigure.cpp
@@ -17,6 +17,7 @@ 
 #include <libcamera/base/timer.h>
 
 #include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
 
 #include "camera_test.h"
 #include "test.h"
@@ -78,7 +79,9 @@  private:
 		 * same buffer allocation for each run.
 		 */
 		if (!allocated_) {
-			int ret = allocator_->allocate(stream);
+			unsigned int bufferCount =
+				camera_->properties().get(properties::MinimumRequests).value();
+			int ret = allocator_->allocate(stream, bufferCount);
 			if (ret < 0) {
 				cerr << "Failed to allocate buffers" << endl;
 				return TestFail;
diff --git a/test/camera/capture.cpp b/test/camera/capture.cpp
index de824083..64d3a8e7 100644
--- a/test/camera/capture.cpp
+++ b/test/camera/capture.cpp
@@ -7,12 +7,13 @@ 
 
 #include <iostream>
 
-#include <libcamera/framebuffer_allocator.h>
-
 #include <libcamera/base/event_dispatcher.h>
 #include <libcamera/base/thread.h>
 #include <libcamera/base/timer.h>
 
+#include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
+
 #include "camera_test.h"
 #include "test.h"
 
@@ -98,8 +99,10 @@  protected:
 
 		Stream *stream = cfg.stream();
 
-		int ret = allocator_->allocate(stream);
-		if (ret < 0)
+		unsigned int bufferCount =
+			camera_->properties().get(properties::MinimumRequests).value();
+		int ret = allocator_->allocate(stream, bufferCount);
+		if (ret < static_cast<int>(bufferCount))
 			return TestFail;
 
 		for (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {
diff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp
index 9c2b0c6a..a9ddb323 100644
--- a/test/camera/statemachine.cpp
+++ b/test/camera/statemachine.cpp
@@ -8,6 +8,7 @@ 
 #include <iostream>
 
 #include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
 
 #include "camera_test.h"
 #include "test.h"
@@ -120,7 +121,9 @@  protected:
 		/* Use internally allocated buffers. */
 		allocator_ = new FrameBufferAllocator(camera_);
 		Stream *stream = *camera_->streams().begin();
-		if (allocator_->allocate(stream) < 0)
+		unsigned int bufferCount =
+			camera_->properties().get(properties::MinimumRequests).value();
+		if (allocator_->allocate(stream, bufferCount) < 0)
 			return TestFail;
 
 		if (camera_->start())
diff --git a/test/fence.cpp b/test/fence.cpp
index 1e38bc2f..88ce2857 100644
--- a/test/fence.cpp
+++ b/test/fence.cpp
@@ -18,6 +18,7 @@ 
 
 #include <libcamera/fence.h>
 #include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
 
 #include "camera_test.h"
 #include "test.h"
@@ -117,8 +118,11 @@  int FenceTest::init()
 	StreamConfiguration &cfg = config_->at(0);
 	stream_ = cfg.stream();
 
+	unsigned int bufferCount =
+		camera_->properties().get(properties::MinimumRequests).value();
+
 	allocator_ = std::make_unique<FrameBufferAllocator>(camera_);
-	if (allocator_->allocate(stream_) < 0)
+	if (allocator_->allocate(stream_, bufferCount) < 0)
 		return TestFail;
 
 	nbuffers_ = allocator_->buffers(stream_).size();
diff --git a/test/mapped-buffer.cpp b/test/mapped-buffer.cpp
index b4422f7d..1b98feea 100644
--- a/test/mapped-buffer.cpp
+++ b/test/mapped-buffer.cpp
@@ -8,6 +8,7 @@ 
 #include <iostream>
 
 #include <libcamera/framebuffer_allocator.h>
+#include <libcamera/property_ids.h>
 
 #include "libcamera/internal/mapped_framebuffer.h"
 
@@ -55,7 +56,9 @@  protected:
 
 		stream_ = cfg.stream();
 
-		int ret = allocator_->allocate(stream_);
+		unsigned int bufferCount =
+			camera_->properties().get(properties::MinimumRequests).value();
+		int ret = allocator_->allocate(stream_, bufferCount);
 		if (ret < 0)
 			return TestFail;