{"id":23274,"url":"https://patchwork.libcamera.org/api/1.1/patches/23274/?format=json","web_url":"https://patchwork.libcamera.org/patch/23274/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20250428090413.38234-3-s.pueschel@pengutronix.de>","date":"2025-04-28T09:02:27","name":"[v11,02/19] libcamera: framebuffer_allocator: Make allocate() require count","commit_ref":null,"pull_url":null,"state":"superseded","archived":false,"hash":"20268d74ace61740718e199a79a885ded391bc28","submitter":{"id":225,"url":"https://patchwork.libcamera.org/api/1.1/people/225/?format=json","name":"Sven Püschel","email":"s.pueschel@pengutronix.de"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/23274/mbox/","series":[{"id":5148,"url":"https://patchwork.libcamera.org/api/1.1/series/5148/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=5148","date":"2025-04-28T09:02:25","name":"lc-compliance: Add test to queue more requests than hardware depth","version":11,"mbox":"https://patchwork.libcamera.org/series/5148/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/23274/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/23274/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 89556C32A2\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 28 Apr 2025 09:05:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B7DB168AD5;\n\tMon, 28 Apr 2025 11:05:12 +0200 (CEST)","from metis.whiteo.stw.pengutronix.de\n\t(metis.whiteo.stw.pengutronix.de [IPv6:2a0a:edc0:2:b01:1d::104])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 45B9168AD0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 28 Apr 2025 11:05:06 +0200 (CEST)","from ptz.office.stw.pengutronix.de ([2a0a:edc0:0:900:1d::77]\n\thelo=peter.guest.stw.pengutronix.de)\n\tby metis.whiteo.stw.pengutronix.de with esmtp (Exim 4.92)\n\t(envelope-from <s.pueschel@pengutronix.de>)\n\tid 1u9KQD-0001au-UX; Mon, 28 Apr 2025 11:05:06 +0200"],"From":"=?utf-8?q?Sven_P=C3=BCschel?= <s.pueschel@pengutronix.de>","To":"libcamera-devel@lists.libcamera.org","Cc":"=?utf-8?b?TsOtY29sYXMgRi4gUi4gQS4gUHJhZG8=?= <nfraprado@collabora.com>, \n\tKieran Bingham <kieran.bingham@ideasonboard.com>, Laurent Pinchart\n\t<laurent.pinchart@ideasonboard.com>, Paul Elder\n\t<paul.elder@ideasonboard.com>, Jacopo Mondi <jacopo@jmondi.org>, \n\t=?utf-8?q?Sven_P=C3=BCschel?= <s.pueschel@pengutronix.de>","Subject":"[PATCH v11 02/19] libcamera: framebuffer_allocator: Make allocate()\n\trequire count","Date":"Mon, 28 Apr 2025 11:02:27 +0200","Message-ID":"<20250428090413.38234-3-s.pueschel@pengutronix.de>","X-Mailer":"git-send-email 2.49.0","In-Reply-To":"<20250428090413.38234-1-s.pueschel@pengutronix.de>","References":"<20250428090413.38234-1-s.pueschel@pengutronix.de>","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","X-SA-Exim-Connect-IP":"2a0a:edc0:0:900:1d::77","X-SA-Exim-Mail-From":"s.pueschel@pengutronix.de","X-SA-Exim-Scanned":"No (on metis.whiteo.stw.pengutronix.de);\n\tSAEximRunCond expanded to false","X-PTX-Original-Recipient":"libcamera-devel@lists.libcamera.org","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"From: Nícolas F. R. A. Prado <nfraprado@collabora.com>\n\nMake FrameBufferAllocator::allocate() require a 'count' argument for the\nnumber of buffers to be allocated.\n\nSigned-off-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\nReviewed-by: Paul Elder <paul.elder@ideasonboard.com>\nSigned-off-by: Paul Elder <paul.elder@ideasonboard.com>\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\nSigned-off-by: Sven Püschel <s.pueschel@pengutronix.de>\n\n---\nChanges in v11:\n- add mali-c55 and virtual\n- adapt python binding\n\nChanges in v10:\n- add imx8-isi\n\nChanges in v9:\n- rebased\n- notably, ControlList::get() returns a std::optional, and since the\n  MinimumRequests property is a required property, we can simply\n  get it via .value()\n  - this will be enforced by lc-compliance in a later patch\n\nChanges in v8:\n- Updated application-developer and pipeline-handler guides with new allocate()\n  API and MinimumRequests property\n- Added handling for when allocate() returns less buffers than needed in cam and\n  the capture unit test\n- Improved FrameBufferAllocator::allocate() description\n\nChanges in v6:\n- Changed static_cast to convert 'count' to unsigned int instead of\n  'bufferCount' to int when comparing\n\nChanges in v5:\n- Made sure that qcam allocates at least 2 buffers\n---\n Documentation/guides/application-developer.rst      |  9 +++++++--\n Documentation/guides/pipeline-handler.rst           |  3 +--\n include/libcamera/camera.h                          |  2 +-\n include/libcamera/framebuffer_allocator.h           |  2 +-\n include/libcamera/internal/pipeline_handler.h       |  2 +-\n src/apps/cam/camera_session.cpp                     | 13 +++++++++----\n src/apps/lc-compliance/helpers/capture.cpp          |  5 +++--\n src/apps/qcam/main_window.cpp                       | 10 +++++++++-\n src/gstreamer/gstlibcameraallocator.cpp             |  6 +++++-\n src/libcamera/camera.cpp                            |  4 ++--\n src/libcamera/framebuffer_allocator.cpp             |  9 +++++++--\n src/libcamera/pipeline/imx8-isi/imx8-isi.cpp        |  5 ++---\n src/libcamera/pipeline/ipu3/ipu3.cpp                |  4 ++--\n src/libcamera/pipeline/mali-c55/mali-c55.cpp        |  3 ++-\n src/libcamera/pipeline/rkisp1/rkisp1.cpp            |  4 ++--\n src/libcamera/pipeline/rpi/common/pipeline_base.cpp |  2 +-\n src/libcamera/pipeline/rpi/common/pipeline_base.h   |  2 +-\n src/libcamera/pipeline/simple/simple.cpp            |  4 ++--\n src/libcamera/pipeline/uvcvideo/uvcvideo.cpp        |  7 ++++---\n src/libcamera/pipeline/vimc/vimc.cpp                |  7 ++++---\n src/libcamera/pipeline/virtual/virtual.cpp          |  5 +++--\n src/libcamera/pipeline_handler.cpp                  |  1 +\n src/py/libcamera/py_main.cpp                        |  4 ++--\n src/v4l2/v4l2_camera.cpp                            |  2 +-\n test/camera/camera_reconfigure.cpp                  |  5 ++++-\n test/camera/capture.cpp                             | 11 +++++++----\n test/camera/statemachine.cpp                        |  5 ++++-\n test/fence.cpp                                      |  6 +++++-\n test/mapped-buffer.cpp                              |  5 ++++-\n 29 files changed, 97 insertions(+), 50 deletions(-)","diff":"diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst\nindex f3798d17..dbd2bfc6 100644\n--- a/Documentation/guides/application-developer.rst\n+++ b/Documentation/guides/application-developer.rst\n@@ -32,6 +32,7 @@ defined names and types without the need of prefixing them.\n    #include <thread>\n \n    #include <libcamera/libcamera.h>\n+   #include <libcamera/property_ids.h>\n \n    using namespace libcamera;\n    using namespace std::chrono_literals;\n@@ -276,7 +277,10 @@ Using the libcamera ``FrameBufferAllocator``\n \n Applications create a ``FrameBufferAllocator`` for a Camera and use it\n to allocate buffers for streams of a ``CameraConfiguration`` with the\n-``allocate()`` function.\n+``allocate()`` function. The number of buffers to be allocated needs to be\n+specified, and should be at least equal to the value of the ``MinimumRequests``\n+property in order for the pipeline to have enough requests to be able to\n+capture without frame drops.\n \n The list of allocated buffers can be retrieved using the ``Stream`` instance\n as the parameter of the ``FrameBufferAllocator::buffers()`` function.\n@@ -284,9 +288,10 @@ as the parameter of the ``FrameBufferAllocator::buffers()`` function.\n .. code:: cpp\n \n    FrameBufferAllocator *allocator = new FrameBufferAllocator(camera);\n+   unsigned int bufferCount = camera->properties().get(properties::MinimumRequests);\n \n    for (StreamConfiguration &cfg : *config) {\n-       int ret = allocator->allocate(cfg.stream());\n+       int ret = allocator->allocate(cfg.stream(), bufferCount);\n        if (ret < 0) {\n            std::cerr << \"Can't allocate buffers\" << std::endl;\n            return -ENOMEM;\ndiff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst\nindex 1079d998..2e9825dc 100644\n--- a/Documentation/guides/pipeline-handler.rst\n+++ b/Documentation/guides/pipeline-handler.rst\n@@ -209,7 +209,7 @@ implementations for the overridden class members.\n           Span<const StreamRole> roles) override;\n           int configure(Camera *camera, CameraConfiguration *config) override;\n \n-          int exportFrameBuffers(Camera *camera, Stream *stream,\n+          int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n           std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n           int start(Camera *camera, const ControlList *controls) override;\n@@ -1242,7 +1242,6 @@ handle this:\n \n .. code-block:: cpp\n \n-   unsigned int count = stream->configuration().bufferCount;\n    VividCameraData *data = cameraData(camera);\n \n    return data->video_->exportBuffers(count, buffers);\ndiff --git a/include/libcamera/camera.h b/include/libcamera/camera.h\nindex 94cee7bd..ce9d8812 100644\n--- a/include/libcamera/camera.h\n+++ b/include/libcamera/camera.h\n@@ -163,7 +163,7 @@ private:\n \tvoid requestComplete(Request *request);\n \n \tfriend class FrameBufferAllocator;\n-\tint exportFrameBuffers(Stream *stream,\n+\tint exportFrameBuffers(Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers);\n };\n \ndiff --git a/include/libcamera/framebuffer_allocator.h b/include/libcamera/framebuffer_allocator.h\nindex f3896bf2..1568c55d 100644\n--- a/include/libcamera/framebuffer_allocator.h\n+++ b/include/libcamera/framebuffer_allocator.h\n@@ -25,7 +25,7 @@ public:\n \tFrameBufferAllocator(std::shared_ptr<Camera> camera);\n \t~FrameBufferAllocator();\n \n-\tint allocate(Stream *stream);\n+\tint allocate(Stream *stream, unsigned int count);\n \tint free(Stream *stream);\n \n \tbool allocated() const { return !buffers_.empty(); }\ndiff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\nindex 972a2fa6..0e9fe23e 100644\n--- a/include/libcamera/internal/pipeline_handler.h\n+++ b/include/libcamera/internal/pipeline_handler.h\n@@ -48,7 +48,7 @@ public:\n \t\t\t\t\t\t\t\t\t   Span<const StreamRole> roles) = 0;\n \tvirtual int configure(Camera *camera, CameraConfiguration *config) = 0;\n \n-\tvirtual int exportFrameBuffers(Camera *camera, Stream *stream,\n+\tvirtual int exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;\n \n \tvirtual int start(Camera *camera, const ControlList *controls) = 0;\ndiff --git a/src/apps/cam/camera_session.cpp b/src/apps/cam/camera_session.cpp\nindex 97c1ae44..f50108f3 100644\n--- a/src/apps/cam/camera_session.cpp\n+++ b/src/apps/cam/camera_session.cpp\n@@ -326,17 +326,22 @@ int CameraSession::startCapture()\n {\n \tint ret;\n \n-\t/* Identify the stream with the least number of buffers. */\n-\tunsigned int nbuffers = UINT_MAX;\n+\tunsigned int nbuffers =\n+\t\tcamera_->properties().get(properties::MinimumRequests).value();\n+\n \tfor (StreamConfiguration &cfg : *config_) {\n-\t\tret = allocator_->allocate(cfg.stream());\n+\t\tret = allocator_->allocate(cfg.stream(), nbuffers);\n \t\tif (ret < 0) {\n \t\t\tstd::cerr << \"Can't allocate buffers\" << std::endl;\n \t\t\treturn -ENOMEM;\n \t\t}\n \n \t\tunsigned int allocated = allocator_->buffers(cfg.stream()).size();\n-\t\tnbuffers = std::min(nbuffers, allocated);\n+\t\tif (allocated < nbuffers) {\n+\t\t\tstd::cerr << \"Unable to allocate enough buffers\"\n+\t\t\t\t  << std::endl;\n+\t\t\treturn -ENOMEM;\n+\t\t}\n \t}\n \n \t/*\ndiff --git a/src/apps/lc-compliance/helpers/capture.cpp b/src/apps/lc-compliance/helpers/capture.cpp\nindex 2a3fa3b6..690de005 100644\n--- a/src/apps/lc-compliance/helpers/capture.cpp\n+++ b/src/apps/lc-compliance/helpers/capture.cpp\n@@ -120,7 +120,8 @@ void Capture::start()\n \tassert(!allocator_.allocated());\n \tassert(requests_.empty());\n \n-\tconst auto bufferCount = config_->at(0).bufferCount;\n+\tunsigned int bufferCount =\n+\t\tcamera_->properties().get(properties::MinimumRequests).value();\n \n \t/* No point in testing less requests then the camera depth. */\n \tif (queueLimit_ && *queueLimit_ < bufferCount) {\n@@ -137,7 +138,7 @@ void Capture::start()\n \tfor (const auto &cfg : *config_) {\n \t\tStream *stream = cfg.stream();\n \n-\t\tint count = allocator_.allocate(stream);\n+\t\tint count = allocator_.allocate(stream, bufferCount);\n \t\tASSERT_GE(count, 0) << \"Failed to allocate buffers\";\n \n \t\tconst auto &buffers = allocator_.buffers(stream);\ndiff --git a/src/apps/qcam/main_window.cpp b/src/apps/qcam/main_window.cpp\nindex d2ccbd23..9a9b7fa9 100644\n--- a/src/apps/qcam/main_window.cpp\n+++ b/src/apps/qcam/main_window.cpp\n@@ -12,6 +12,7 @@\n #include <string>\n \n #include <libcamera/camera_manager.h>\n+#include <libcamera/property_ids.h>\n #include <libcamera/version.h>\n \n #include <QCoreApplication>\n@@ -447,7 +448,14 @@ int MainWindow::startCapture()\n \tfor (StreamConfiguration &config : *config_) {\n \t\tStream *stream = config.stream();\n \n-\t\tret = allocator_->allocate(stream);\n+\t\t/*\n+\t\t * We hold on to a buffer for display, so need one extra from\n+\t\t * the minimum required for capture.\n+\t\t */\n+\t\tunsigned int bufferCount =\n+\t\t\tcamera_->properties().get(properties::MinimumRequests).value() + 1;\n+\n+\t\tret = allocator_->allocate(stream, bufferCount);\n \t\tif (ret < 0) {\n \t\t\tqWarning() << \"Failed to allocate capture buffers\";\n \t\t\tgoto error;\ndiff --git a/src/gstreamer/gstlibcameraallocator.cpp b/src/gstreamer/gstlibcameraallocator.cpp\nindex d4492d99..0d8e4c40 100644\n--- a/src/gstreamer/gstlibcameraallocator.cpp\n+++ b/src/gstreamer/gstlibcameraallocator.cpp\n@@ -12,6 +12,7 @@\n \n #include <libcamera/camera.h>\n #include <libcamera/framebuffer_allocator.h>\n+#include <libcamera/property_ids.h>\n #include <libcamera/stream.h>\n \n #include \"gstlibcamera-utils.h\"\n@@ -209,11 +210,14 @@ gst_libcamera_allocator_new(std::shared_ptr<Camera> camera,\n \tif (ret)\n \t\treturn nullptr;\n \n+\tunsigned int bufferCount =\n+\t\tcamera->properties().get(properties::MinimumRequests).value();\n+\n \tself->fb_allocator = new FrameBufferAllocator(camera);\n \tfor (StreamConfiguration &streamCfg : *config_) {\n \t\tStream *stream = streamCfg.stream();\n \n-\t\tret = self->fb_allocator->allocate(stream);\n+\t\tret = self->fb_allocator->allocate(stream, bufferCount);\n \t\tif (ret <= 0)\n \t\t\treturn nullptr;\n \ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex c180a5fd..33e928b9 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -950,7 +950,7 @@ void Camera::disconnect()\n \tdisconnected.emit();\n }\n \n-int Camera::exportFrameBuffers(Stream *stream,\n+int Camera::exportFrameBuffers(Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tPrivate *const d = _d();\n@@ -967,7 +967,7 @@ int Camera::exportFrameBuffers(Stream *stream,\n \n \treturn d->pipe_->invokeMethod(&PipelineHandler::exportFrameBuffers,\n \t\t\t\t      ConnectionTypeBlocking, this, stream,\n-\t\t\t\t      buffers);\n+\t\t\t\t      count, buffers);\n }\n \n /**\ndiff --git a/src/libcamera/framebuffer_allocator.cpp b/src/libcamera/framebuffer_allocator.cpp\nindex 3d53bde2..c522f479 100644\n--- a/src/libcamera/framebuffer_allocator.cpp\n+++ b/src/libcamera/framebuffer_allocator.cpp\n@@ -68,6 +68,7 @@ FrameBufferAllocator::~FrameBufferAllocator() = default;\n /**\n  * \\brief Allocate buffers for a configured stream\n  * \\param[in] stream The stream to allocate buffers for\n+ * \\param[in] count The number of buffers to allocate\n  *\n  * Allocate buffers suitable for capturing frames from the \\a stream. The Camera\n  * shall have been previously configured with Camera::configure() and shall be\n@@ -76,6 +77,10 @@ FrameBufferAllocator::~FrameBufferAllocator() = default;\n  * Upon successful allocation, the allocated buffers can be retrieved with the\n  * buffers() function.\n  *\n+ * This function may allocate less buffers than requested, due to memory and\n+ * other system constraints. The caller shall always check the return value to\n+ * verify if the number of allocate buffers matches its needs.\n+ *\n  * \\return The number of allocated buffers on success or a negative error code\n  * otherwise\n  * \\retval -EACCES The camera is not in a state where buffers can be allocated\n@@ -83,7 +88,7 @@ FrameBufferAllocator::~FrameBufferAllocator() = default;\n  * not part of the active camera configuration\n  * \\retval -EBUSY Buffers are already allocated for the \\a stream\n  */\n-int FrameBufferAllocator::allocate(Stream *stream)\n+int FrameBufferAllocator::allocate(Stream *stream, unsigned int count)\n {\n \tconst auto &[it, inserted] = buffers_.try_emplace(stream);\n \n@@ -92,7 +97,7 @@ int FrameBufferAllocator::allocate(Stream *stream)\n \t\treturn -EBUSY;\n \t}\n \n-\tint ret = camera_->exportFrameBuffers(stream, &it->second);\n+\tint ret = camera_->exportFrameBuffers(stream, count, &it->second);\n \tif (ret == -EINVAL)\n \t\tLOG(Allocator, Error)\n \t\t\t<< \"Stream is not part of \" << camera_->id()\ndiff --git a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp\nindex 2b8a583e..21f44424 100644\n--- a/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp\n+++ b/src/libcamera/pipeline/imx8-isi/imx8-isi.cpp\n@@ -108,7 +108,7 @@ public:\n \tgenerateConfiguration(Camera *camera, Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -908,10 +908,9 @@ int PipelineHandlerISI::configure(Camera *camera, CameraConfiguration *c)\n \treturn 0;\n }\n \n-int PipelineHandlerISI::exportFrameBuffers(Camera *camera, Stream *stream,\n+int PipelineHandlerISI::exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n-\tunsigned int count = stream->configuration().bufferCount;\n \tPipe *pipe = pipeFromStream(camera, stream);\n \n \treturn pipe->capture->exportBuffers(count, buffers);\ndiff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\nindex 356ca2e1..30b74d4e 100644\n--- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n@@ -139,7 +139,7 @@ public:\n \t\t\t\t\t\t\t\t   Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -637,10 +637,10 @@ int PipelineHandlerIPU3::configure(Camera *camera, CameraConfiguration *c)\n }\n \n int PipelineHandlerIPU3::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t    unsigned int count,\n \t\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tIPU3CameraData *data = cameraData(camera);\n-\tunsigned int count = stream->configuration().bufferCount;\n \n \tif (stream == &data->outStream_)\n \t\treturn data->imgu_->output_->exportBuffers(count, buffers);\ndiff --git a/src/libcamera/pipeline/mali-c55/mali-c55.cpp b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\nindex f0ab3d2e..9df1622e 100644\n--- a/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n+++ b/src/libcamera/pipeline/mali-c55/mali-c55.cpp\n@@ -608,6 +608,7 @@ public:\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n \tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t       unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \tint allocateBuffers(Camera *camera);\n \tvoid freeBuffers(Camera *camera);\n@@ -1087,10 +1088,10 @@ int PipelineHandlerMaliC55::configure(Camera *camera,\n }\n \n int PipelineHandlerMaliC55::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t       unsigned int count,\n \t\t\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tMaliC55Pipe *pipe = pipeFromStream(cameraData(camera), stream);\n-\tunsigned int count = stream->configuration().bufferCount;\n \n \treturn pipe->cap->exportBuffers(count, buffers);\n }\ndiff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\nindex 5e3a4dba..4e8eff70 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -158,7 +158,7 @@ public:\n \t\t\t\t\t\t\t\t   Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -953,10 +953,10 @@ int PipelineHandlerRkISP1::configure(Camera *camera, CameraConfiguration *c)\n }\n \n int PipelineHandlerRkISP1::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream,\n+\t\t\t\t\t      unsigned int count,\n \t\t\t\t\t      std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tRkISP1CameraData *data = cameraData(camera);\n-\tunsigned int count = stream->configuration().bufferCount;\n \n \tif (stream == &data->mainPathStream_) {\n \t\t/*\ndiff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\nindex ef51d530..dc1e0a2e 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp\n@@ -630,10 +630,10 @@ int PipelineHandlerBase::configure(Camera *camera, CameraConfiguration *config)\n }\n \n int PipelineHandlerBase::exportFrameBuffers([[maybe_unused]] Camera *camera, libcamera::Stream *stream,\n+\t\t\t\t\t    unsigned int count,\n \t\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tRPi::Stream *s = static_cast<RPi::Stream *>(stream);\n-\tunsigned int count = stream->configuration().bufferCount;\n \tint ret = s->dev()->exportBuffers(count, buffers);\n \n \ts->setExportedBuffers(buffers);\ndiff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h\nindex aae0c2f3..e73d6f18 100644\n--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h\n+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h\n@@ -218,7 +218,7 @@ public:\n \tgenerateConfiguration(Camera *camera, Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, libcamera::Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, libcamera::Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\ndiff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp\nindex 7abf0fc9..1e77ccdc 100644\n--- a/src/libcamera/pipeline/simple/simple.cpp\n+++ b/src/libcamera/pipeline/simple/simple.cpp\n@@ -404,7 +404,7 @@ public:\n \t\t\t\t\t\t\t\t   Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -1434,10 +1434,10 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)\n }\n \n int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,\n+\t\t\t\t\t      unsigned int count,\n \t\t\t\t\t      std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tSimpleCameraData *data = cameraData(camera);\n-\tunsigned int count = stream->configuration().bufferCount;\n \n \t/*\n \t * Export buffers on the converter or capture video node, depending on\ndiff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\nindex 31951a12..60c82364 100644\n--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp\n@@ -87,7 +87,7 @@ public:\n \t\t\t\t\t\t\t\t   Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -278,11 +278,12 @@ int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config)\n \treturn 0;\n }\n \n-int PipelineHandlerUVC::exportFrameBuffers(Camera *camera, Stream *stream,\n+int PipelineHandlerUVC::exportFrameBuffers(Camera *camera,\n+\t\t\t\t\t   [[maybe_unused]] Stream *stream,\n+\t\t\t\t\t   unsigned int count,\n \t\t\t\t\t   std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tUVCCameraData *data = cameraData(camera);\n-\tunsigned int count = stream->configuration().bufferCount;\n \n \treturn data->video_->exportBuffers(count, buffers);\n }\ndiff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp\nindex 01685a64..8833647a 100644\n--- a/src/libcamera/pipeline/vimc/vimc.cpp\n+++ b/src/libcamera/pipeline/vimc/vimc.cpp\n@@ -92,7 +92,7 @@ public:\n \t\t\t\t\t\t\t\t   Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -343,11 +343,12 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)\n \treturn 0;\n }\n \n-int PipelineHandlerVimc::exportFrameBuffers(Camera *camera, Stream *stream,\n+int PipelineHandlerVimc::exportFrameBuffers(Camera *camera,\n+\t\t\t\t\t    [[maybe_unused]] Stream *stream,\n+\t\t\t\t\t    unsigned int count,\n \t\t\t\t\t    std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tVimcCameraData *data = cameraData(camera);\n-\tunsigned int count = stream->configuration().bufferCount;\n \n \treturn data->video_->exportBuffers(count, buffers);\n }\ndiff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp\nindex c33f1a5e..ad2bdb57 100644\n--- a/src/libcamera/pipeline/virtual/virtual.cpp\n+++ b/src/libcamera/pipeline/virtual/virtual.cpp\n@@ -87,7 +87,7 @@ public:\n \t\t\t\t\t\t\t\t   Span<const StreamRole> roles) override;\n \tint configure(Camera *camera, CameraConfiguration *config) override;\n \n-\tint exportFrameBuffers(Camera *camera, Stream *stream,\n+\tint exportFrameBuffers(Camera *camera, Stream *stream, unsigned int count,\n \t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n \n \tint start(Camera *camera, const ControlList *controls) override;\n@@ -269,6 +269,7 @@ int PipelineHandlerVirtual::configure(Camera *camera,\n \n int PipelineHandlerVirtual::exportFrameBuffers([[maybe_unused]] Camera *camera,\n \t\t\t\t\t       Stream *stream,\n+\t\t\t\t\t       unsigned int count,\n \t\t\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n {\n \tif (!dmaBufAllocator_.isValid())\n@@ -281,7 +282,7 @@ int PipelineHandlerVirtual::exportFrameBuffers([[maybe_unused]] Camera *camera,\n \tfor (size_t i = 0; i < info.numPlanes(); ++i)\n \t\tplaneSizes.push_back(info.planeSize(config.size, i));\n \n-\treturn dmaBufAllocator_.exportBuffers(config.bufferCount, planeSizes, buffers);\n+\treturn dmaBufAllocator_.exportBuffers(count, planeSizes, buffers);\n }\n \n int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera,\ndiff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex d84dff3c..3211256c 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -314,6 +314,7 @@ void PipelineHandler::unlockMediaDevices()\n  * \\brief Allocate and export buffers for \\a stream\n  * \\param[in] camera The camera\n  * \\param[in] stream The stream to allocate buffers for\n+ * \\param[in] count The number of buffers to allocate\n  * \\param[out] buffers Array of buffers successfully allocated\n  *\n  * This function allocates buffers for the \\a stream from the devices associated\ndiff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp\nindex 441a70ab..f75450ee 100644\n--- a/src/py/libcamera/py_main.cpp\n+++ b/src/py/libcamera/py_main.cpp\n@@ -349,8 +349,8 @@ PYBIND11_MODULE(_libcamera, m)\n \n \tpyFrameBufferAllocator\n \t\t.def(py::init<PyCameraSmartPtr<Camera>>(), py::keep_alive<1, 2>())\n-\t\t.def(\"allocate\", [](FrameBufferAllocator &self, Stream *stream) {\n-\t\t\tint ret = self.allocate(stream);\n+\t\t.def(\"allocate\", [](FrameBufferAllocator &self, Stream *stream, unsigned int count) {\n+\t\t\tint ret = self.allocate(stream, count);\n \t\t\tif (ret < 0)\n \t\t\t\tthrow std::system_error(-ret, std::generic_category(),\n \t\t\t\t\t\t\t\"Failed to allocate buffers\");\ndiff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\nindex 94d138cd..a4b0e562 100644\n--- a/src/v4l2/v4l2_camera.cpp\n+++ b/src/v4l2/v4l2_camera.cpp\n@@ -162,7 +162,7 @@ int V4L2Camera::allocBuffers(unsigned int count)\n {\n \tStream *stream = config_->at(0).stream();\n \n-\tint ret = bufferAllocator_->allocate(stream);\n+\tint ret = bufferAllocator_->allocate(stream, count);\n \tif (ret < 0)\n \t\treturn ret;\n \ndiff --git a/test/camera/camera_reconfigure.cpp b/test/camera/camera_reconfigure.cpp\nindex 06c87730..63a97863 100644\n--- a/test/camera/camera_reconfigure.cpp\n+++ b/test/camera/camera_reconfigure.cpp\n@@ -17,6 +17,7 @@\n #include <libcamera/base/timer.h>\n \n #include <libcamera/framebuffer_allocator.h>\n+#include <libcamera/property_ids.h>\n \n #include \"camera_test.h\"\n #include \"test.h\"\n@@ -78,7 +79,9 @@ private:\n \t\t * same buffer allocation for each run.\n \t\t */\n \t\tif (!allocated_) {\n-\t\t\tint ret = allocator_->allocate(stream);\n+\t\t\tunsigned int bufferCount =\n+\t\t\t\tcamera_->properties().get(properties::MinimumRequests).value();\n+\t\t\tint ret = allocator_->allocate(stream, bufferCount);\n \t\t\tif (ret < 0) {\n \t\t\t\tcerr << \"Failed to allocate buffers\" << endl;\n \t\t\t\treturn TestFail;\ndiff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\nindex 8766fb19..05f4a1e3 100644\n--- a/test/camera/capture.cpp\n+++ b/test/camera/capture.cpp\n@@ -7,12 +7,13 @@\n \n #include <iostream>\n \n-#include <libcamera/framebuffer_allocator.h>\n-\n #include <libcamera/base/event_dispatcher.h>\n #include <libcamera/base/thread.h>\n #include <libcamera/base/timer.h>\n \n+#include <libcamera/framebuffer_allocator.h>\n+#include <libcamera/property_ids.h>\n+\n #include \"camera_test.h\"\n #include \"test.h\"\n \n@@ -101,8 +102,10 @@ protected:\n \n \t\tStream *stream = cfg.stream();\n \n-\t\tint ret = allocator_->allocate(stream);\n-\t\tif (ret < 0)\n+\t\tunsigned int bufferCount =\n+\t\t\tcamera_->properties().get(properties::MinimumRequests).value();\n+\t\tint ret = allocator_->allocate(stream, bufferCount);\n+\t\tif (ret < static_cast<int>(bufferCount))\n \t\t\treturn TestFail;\n \n \t\tfor (const std::unique_ptr<FrameBuffer> &buffer : allocator_->buffers(stream)) {\ndiff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp\nindex 9c2b0c6a..a9ddb323 100644\n--- a/test/camera/statemachine.cpp\n+++ b/test/camera/statemachine.cpp\n@@ -8,6 +8,7 @@\n #include <iostream>\n \n #include <libcamera/framebuffer_allocator.h>\n+#include <libcamera/property_ids.h>\n \n #include \"camera_test.h\"\n #include \"test.h\"\n@@ -120,7 +121,9 @@ protected:\n \t\t/* Use internally allocated buffers. */\n \t\tallocator_ = new FrameBufferAllocator(camera_);\n \t\tStream *stream = *camera_->streams().begin();\n-\t\tif (allocator_->allocate(stream) < 0)\n+\t\tunsigned int bufferCount =\n+\t\t\tcamera_->properties().get(properties::MinimumRequests).value();\n+\t\tif (allocator_->allocate(stream, bufferCount) < 0)\n \t\t\treturn TestFail;\n \n \t\tif (camera_->start())\ndiff --git a/test/fence.cpp b/test/fence.cpp\nindex 8095b228..eb6c8b54 100644\n--- a/test/fence.cpp\n+++ b/test/fence.cpp\n@@ -18,6 +18,7 @@\n \n #include <libcamera/fence.h>\n #include <libcamera/framebuffer_allocator.h>\n+#include <libcamera/property_ids.h>\n \n #include \"camera_test.h\"\n #include \"test.h\"\n@@ -119,8 +120,11 @@ int FenceTest::init()\n \tStreamConfiguration &cfg = config_->at(0);\n \tstream_ = cfg.stream();\n \n+\tunsigned int bufferCount =\n+\t\tcamera_->properties().get(properties::MinimumRequests).value();\n+\n \tallocator_ = std::make_unique<FrameBufferAllocator>(camera_);\n-\tif (allocator_->allocate(stream_) < 0)\n+\tif (allocator_->allocate(stream_, bufferCount) < 0)\n \t\treturn TestFail;\n \n \tnbuffers_ = allocator_->buffers(stream_).size();\ndiff --git a/test/mapped-buffer.cpp b/test/mapped-buffer.cpp\nindex b4422f7d..1b98feea 100644\n--- a/test/mapped-buffer.cpp\n+++ b/test/mapped-buffer.cpp\n@@ -8,6 +8,7 @@\n #include <iostream>\n \n #include <libcamera/framebuffer_allocator.h>\n+#include <libcamera/property_ids.h>\n \n #include \"libcamera/internal/mapped_framebuffer.h\"\n \n@@ -55,7 +56,9 @@ protected:\n \n \t\tstream_ = cfg.stream();\n \n-\t\tint ret = allocator_->allocate(stream_);\n+\t\tunsigned int bufferCount =\n+\t\t\tcamera_->properties().get(properties::MinimumRequests).value();\n+\t\tint ret = allocator_->allocate(stream_, bufferCount);\n \t\tif (ret < 0)\n \t\t\treturn TestFail;\n \n","prefixes":["v11","02/19"]}