Patch Detail
Show a patch.
GET /api/1.1/patches/2375/?format=api
{ "id": 2375, "url": "https://patchwork.libcamera.org/api/1.1/patches/2375/?format=api", "web_url": "https://patchwork.libcamera.org/patch/2375/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api", "name": "libcamera", "link_name": "libcamera", "list_id": "libcamera_core", "list_email": "libcamera-devel@lists.libcamera.org", "web_url": "", "scm_url": "", "webscm_url": "" }, "msgid": "<20191126233620.1695316-28-niklas.soderlund@ragnatech.se>", "date": "2019-11-26T23:36:17", "name": "[libcamera-devel,27/30] libcamera: Switch to FrameBuffer interface", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "70d017298df63ed0532eba36e4718279d84c7781", "submitter": { "id": 5, "url": "https://patchwork.libcamera.org/api/1.1/people/5/?format=api", "name": "Niklas Söderlund", "email": "niklas.soderlund@ragnatech.se" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/2375/mbox/", "series": [ { "id": 579, "url": "https://patchwork.libcamera.org/api/1.1/series/579/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=579", "date": "2019-11-26T23:35:50", "name": "libcamera: Rework buffer API", "version": 1, "mbox": "https://patchwork.libcamera.org/series/579/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/2375/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/2375/checks/", "tags": {}, "headers": { "Return-Path": "<niklas.soderlund@ragnatech.se>", "Received": [ "from bin-mail-out-06.binero.net (bin-mail-out-06.binero.net\n\t[195.74.38.229])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5F66260C3D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 27 Nov 2019 00:39:49 +0100 (CET)", "from bismarck.berto.se (p54ac5865.dip0.t-ipconnect.de\n\t[84.172.88.101]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA\n\tid 0676cd8e-10a6-11ea-a0b9-005056917f90;\n\tWed, 27 Nov 2019 00:39:44 +0100 (CET)" ], "X-Halon-ID": "0676cd8e-10a6-11ea-a0b9-005056917f90", "Authorized-sender": "niklas@soderlund.pp.se", "From": "=?utf-8?q?Niklas_S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Wed, 27 Nov 2019 00:36:17 +0100", "Message-Id": "<20191126233620.1695316-28-niklas.soderlund@ragnatech.se>", "X-Mailer": "git-send-email 2.24.0", "In-Reply-To": "<20191126233620.1695316-1-niklas.soderlund@ragnatech.se>", "References": "<20191126233620.1695316-1-niklas.soderlund@ragnatech.se>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=UTF-8", "Content-Transfer-Encoding": "8bit", "Subject": "[libcamera-devel] [PATCH 27/30] libcamera: Switch to FrameBuffer\n\tinterface", "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>", "X-List-Received-Date": "Tue, 26 Nov 2019 23:39:49 -0000" }, "content": "Switch to the FrameBuffer interface where all buffers are treated as\nexternal buffers and are allocated outside the camera. Applications\nallocating buffers using libcamera are switched to use the\nBufferAllocator helper.\n\nA follow up changes to this one will finalize the transition to the new\nFrameBuffer interface by removing code that is left unused after this\nchange.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\n include/libcamera/camera.h | 4 +-\n include/libcamera/request.h | 14 +-\n src/android/camera_device.cpp | 19 +-\n src/cam/buffer_writer.cpp | 9 +-\n src/cam/buffer_writer.h | 3 +-\n src/cam/capture.cpp | 38 +-\n src/cam/capture.h | 4 +-\n src/libcamera/camera.cpp | 34 --\n src/libcamera/include/pipeline_handler.h | 5 +-\n src/libcamera/include/v4l2_videodevice.h | 9 +-\n src/libcamera/pipeline/ipu3/ipu3.cpp | 223 +++-------\n src/libcamera/pipeline/rkisp1/rkisp1.cpp | 120 +++--\n src/libcamera/pipeline/uvcvideo.cpp | 20 +-\n src/libcamera/pipeline/vimc.cpp | 20 +-\n src/libcamera/pipeline_handler.cpp | 2 +-\n src/libcamera/request.cpp | 16 +-\n src/libcamera/v4l2_videodevice.cpp | 80 +---\n src/qcam/main_window.cpp | 47 +-\n src/qcam/main_window.h | 6 +-\n test/camera/buffer_import.cpp | 415 +++++-------------\n test/camera/capture.cpp | 31 +-\n test/camera/statemachine.cpp | 12 +-\n test/v4l2_videodevice/buffer_sharing.cpp | 23 +-\n test/v4l2_videodevice/capture_async.cpp | 14 +-\n test/v4l2_videodevice/request_buffers.cpp | 11 +-\n test/v4l2_videodevice/stream_on_off.cpp | 6 +-\n test/v4l2_videodevice/v4l2_m2mdevice.cpp | 40 +-\n test/v4l2_videodevice/v4l2_videodevice_test.h | 2 +-\n 28 files changed, 415 insertions(+), 812 deletions(-)", "diff": "diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h\nindex ef6a37bb142c83a6..02d047b9649f53d0 100644\n--- a/include/libcamera/camera.h\n+++ b/include/libcamera/camera.h\n@@ -19,7 +19,7 @@\n \n namespace libcamera {\n \n-class Buffer;\n+class FrameBuffer;\n class PipelineHandler;\n class Request;\n \n@@ -77,7 +77,7 @@ public:\n \n \tconst std::string &name() const;\n \n-\tSignal<Request *, Buffer *> bufferCompleted;\n+\tSignal<Request *, FrameBuffer *> bufferCompleted;\n \tSignal<Request *> requestCompleted;\n \tSignal<Camera *> disconnected;\n \ndiff --git a/include/libcamera/request.h b/include/libcamera/request.h\nindex a8708010ec1247a2..02c0ef5f6b028bd4 100644\n--- a/include/libcamera/request.h\n+++ b/include/libcamera/request.h\n@@ -17,9 +17,9 @@\n \n namespace libcamera {\n \n-class Buffer;\n class Camera;\n class CameraControlValidator;\n+class FrameBuffer;\n class Stream;\n \n class Request\n@@ -38,9 +38,9 @@ public:\n \n \tControlList &controls() { return *controls_; }\n \tControlList &metadata() { return *metadata_; }\n-\tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n-\tint addBuffer(Stream *stream, std::unique_ptr<Buffer> buffer);\n-\tBuffer *findBuffer(Stream *stream) const;\n+\tconst std::map<Stream *, FrameBuffer *> &buffers() const { return bufferMap_; }\n+\tint addBuffer(Stream *stream, FrameBuffer *buffer);\n+\tFrameBuffer *findBuffer(Stream *stream) const;\n \n \tuint64_t cookie() const { return cookie_; }\n \tStatus status() const { return status_; }\n@@ -54,14 +54,14 @@ private:\n \tint prepare();\n \tvoid complete();\n \n-\tbool completeBuffer(Buffer *buffer);\n+\tbool completeBuffer(FrameBuffer *buffer);\n \n \tCamera *camera_;\n \tCameraControlValidator *validator_;\n \tControlList *controls_;\n \tControlList *metadata_;\n-\tstd::map<Stream *, Buffer *> bufferMap_;\n-\tstd::unordered_set<Buffer *> pending_;\n+\tstd::map<Stream *, FrameBuffer *> bufferMap_;\n+\tstd::unordered_set<FrameBuffer *> pending_;\n \n \tconst uint64_t cookie_;\n \tStatus status_;\ndiff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex 55b29a9a41ab8943..708cf95b0c3c25c2 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -739,13 +739,13 @@ void CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reque\n \t * and (currently) only supported request buffer.\n \t */\n \tconst buffer_handle_t camera3Handle = *camera3Buffers[0].buffer;\n-\tstd::array<int, 3> fds = {\n-\t\tcamera3Handle->data[0],\n-\t\tcamera3Handle->data[1],\n-\t\tcamera3Handle->data[2],\n-\t};\n \n-\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(fds);\n+\tstd::vector<FrameBuffer::Plane> planes;\n+\tplanes.push_back({ .fd = camera3Handle->data[0], .length = 0 });\n+\tplanes.push_back({ .fd = camera3Handle->data[1], .length = 0 });\n+\tplanes.push_back({ .fd = camera3Handle->data[2], .length = 0 });\n+\n+\tFrameBuffer *buffer = new FrameBuffer(planes);\n \tif (!buffer) {\n \t\tLOG(HAL, Error) << \"Failed to create buffer\";\n \t\tdelete descriptor;\n@@ -754,7 +754,7 @@ void CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reque\n \n \tRequest *request =\n \t\tcamera_->createRequest(reinterpret_cast<uint64_t>(descriptor));\n-\trequest->addBuffer(stream, std::move(buffer));\n+\trequest->addBuffer(stream, buffer);\n \n \tint ret = camera_->queueRequest(request);\n \tif (ret) {\n@@ -771,8 +771,8 @@ error:\n \n void CameraDevice::requestComplete(Request *request)\n {\n-\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n-\tBuffer *libcameraBuffer = buffers.begin()->second;\n+\tconst std::map<Stream *, FrameBuffer *> &buffers = request->buffers();\n+\tFrameBuffer *libcameraBuffer = buffers.begin()->second;\n \tcamera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK;\n \tstd::unique_ptr<CameraMetadata> resultMetadata;\n \n@@ -825,6 +825,7 @@ void CameraDevice::requestComplete(Request *request)\n \tcallbacks_->process_capture_result(callbacks_, &captureResult);\n \n \tdelete descriptor;\n+\tdelete libcameraBuffer;\n }\n \n void CameraDevice::notifyShutter(uint32_t frameNumber, uint64_t timestamp)\ndiff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\nindex b6b40baeee661df6..5cead75a16d0923a 100644\n--- a/src/cam/buffer_writer.cpp\n+++ b/src/cam/buffer_writer.cpp\n@@ -21,7 +21,7 @@ BufferWriter::BufferWriter(const std::string &pattern)\n {\n }\n \n-int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n+int BufferWriter::write(FrameBuffer *buffer, const std::string &streamName)\n {\n \tstd::string filename;\n \tsize_t pos;\n@@ -42,10 +42,9 @@ int BufferWriter::write(Buffer *buffer, const std::string &streamName)\n \tif (fd == -1)\n \t\treturn -errno;\n \n-\tBufferMemory *mem = buffer->mem();\n-\tfor (Dmabuf &dmabuf : mem->planes()) {\n-\t\tvoid *data = dmabuf.mem();\n-\t\tunsigned int length = dmabuf.length();\n+\tfor (Dmabuf *dmabuf : buffer->dmabufs()) {\n+\t\tvoid *data = dmabuf->mem();\n+\t\tunsigned int length = dmabuf->length();\n \n \t\tret = ::write(fd, data, length);\n \t\tif (ret < 0) {\ndiff --git a/src/cam/buffer_writer.h b/src/cam/buffer_writer.h\nindex 7bf785d1e83235ff..5917a7dfb5e28106 100644\n--- a/src/cam/buffer_writer.h\n+++ b/src/cam/buffer_writer.h\n@@ -16,7 +16,8 @@ class BufferWriter\n public:\n \tBufferWriter(const std::string &pattern = \"frame-#.bin\");\n \n-\tint write(libcamera::Buffer *buffer, const std::string &streamName);\n+\tint write(libcamera::FrameBuffer *buffer,\n+\t\t const std::string &streamName);\n \n private:\n \tstd::string pattern_;\ndiff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\nindex a4fa88a8d99669bc..154e6e461ee2b96b 100644\n--- a/src/cam/capture.cpp\n+++ b/src/cam/capture.cpp\n@@ -57,7 +57,10 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options)\n \t\t\twriter_ = new BufferWriter();\n \t}\n \n-\tret = capture(loop);\n+\n+\tBufferAllocator allocator(camera_);\n+\n+\tret = capture(loop, allocator);\n \n \tif (options.isSet(OptFile)) {\n \t\tdelete writer_;\n@@ -69,14 +72,22 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options)\n \treturn ret;\n }\n \n-int Capture::capture(EventLoop *loop)\n+int Capture::capture(EventLoop *loop, BufferAllocator &allocator)\n {\n \tint ret;\n \n \t/* Identify the stream with the least number of buffers. */\n \tunsigned int nbuffers = UINT_MAX;\n-\tfor (StreamConfiguration &cfg : *config_)\n-\t\tnbuffers = std::min(nbuffers, cfg.bufferCount);\n+\tfor (StreamConfiguration &cfg : *config_) {\n+\t\tret = allocator.allocate(cfg.stream(), cfg);\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}\n \n \t/*\n \t * TODO: make cam tool smarter to support still capture by for\n@@ -93,9 +104,9 @@ int Capture::capture(EventLoop *loop)\n \n \t\tfor (StreamConfiguration &cfg : *config_) {\n \t\t\tStream *stream = cfg.stream();\n-\t\t\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(i);\n+\t\t\tFrameBuffer *buffer = allocator.buffers(stream)[i];\n \n-\t\t\tret = request->addBuffer(stream, std::move(buffer));\n+\t\t\tret = request->addBuffer(stream, buffer);\n \t\t\tif (ret < 0) {\n \t\t\t\tstd::cerr << \"Can't set buffer for request\"\n \t\t\t\t\t << std::endl;\n@@ -138,7 +149,7 @@ void Capture::requestComplete(Request *request)\n \tif (request->status() == Request::RequestCancelled)\n \t\treturn;\n \n-\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n+\tconst std::map<Stream *, FrameBuffer *> &buffers = request->buffers();\n \n \tstd::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n \tdouble fps = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_).count();\n@@ -151,7 +162,7 @@ void Capture::requestComplete(Request *request)\n \n \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n \t\tStream *stream = it->first;\n-\t\tBuffer *buffer = it->second;\n+\t\tFrameBuffer *buffer = it->second;\n \t\tconst std::string &name = streamName_[stream];\n \n \t\tinfo << \" \" << name\n@@ -180,16 +191,9 @@ void Capture::requestComplete(Request *request)\n \n \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n \t\tStream *stream = it->first;\n-\t\tBuffer *buffer = it->second;\n-\t\tunsigned int index = buffer->index();\n-\n-\t\tstd::unique_ptr<Buffer> newBuffer = stream->createBuffer(index);\n-\t\tif (!newBuffer) {\n-\t\t\tstd::cerr << \"Can't create buffer\" << std::endl;\n-\t\t\treturn;\n-\t\t}\n+\t\tFrameBuffer *buffer = it->second;\n \n-\t\trequest->addBuffer(stream, std::move(newBuffer));\n+\t\trequest->addBuffer(stream, buffer);\n \t}\n \n \tcamera_->queueRequest(request);\ndiff --git a/src/cam/capture.h b/src/cam/capture.h\nindex c692d48918f2de1d..3a8842fbd2080c2c 100644\n--- a/src/cam/capture.h\n+++ b/src/cam/capture.h\n@@ -10,6 +10,8 @@\n #include <chrono>\n #include <memory>\n \n+#include <libcamera/allocator.h>\n+#include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n #include <libcamera/request.h>\n #include <libcamera/stream.h>\n@@ -26,7 +28,7 @@ public:\n \n \tint run(EventLoop *loop, const OptionsParser::Options &options);\n private:\n-\tint capture(EventLoop *loop);\n+\tint capture(EventLoop *loop, libcamera::BufferAllocator &allocator);\n \n \tvoid requestComplete(libcamera::Request *request);\n \ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex 6b1b3fb64f8b2c0b..05fdcaab8f918afa 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -678,12 +678,6 @@ int Camera::configure(CameraConfiguration *config)\n \n \t\tstream->configuration_ = cfg;\n \t\tactiveStreams_.insert(stream);\n-\n-\t\t/*\n-\t\t * Allocate buffer objects in the pool.\n-\t\t * Memory will be allocated and assigned later.\n-\t\t */\n-\t\tstream->createBuffers(cfg.memoryType, cfg.bufferCount);\n \t}\n \n \tstate_ = CameraConfigured;\n@@ -739,14 +733,6 @@ int Camera::freeBuffers()\n \tif (!stateIs(CameraPrepared))\n \t\treturn -EACCES;\n \n-\tfor (Stream *stream : activeStreams_) {\n-\t\t/*\n-\t\t * All mappings must be destroyed before buffers can be freed\n-\t\t * by the V4L2 device that has allocated them.\n-\t\t */\n-\t\tstream->destroyBuffers();\n-\t}\n-\n \tstate_ = CameraConfigured;\n \n \treturn pipe_->freeBuffers(this, activeStreams_);\n@@ -812,24 +798,11 @@ int Camera::queueRequest(Request *request)\n \n \tfor (auto const &it : request->buffers()) {\n \t\tStream *stream = it.first;\n-\t\tBuffer *buffer = it.second;\n \n \t\tif (activeStreams_.find(stream) == activeStreams_.end()) {\n \t\t\tLOG(Camera, Error) << \"Invalid request\";\n \t\t\treturn -EINVAL;\n \t\t}\n-\n-\t\tif (stream->memoryType() == ExternalMemory) {\n-\t\t\tint index = stream->mapBuffer(buffer);\n-\t\t\tif (index < 0) {\n-\t\t\t\tLOG(Camera, Error) << \"No buffer memory available\";\n-\t\t\t\treturn -ENOMEM;\n-\t\t\t}\n-\n-\t\t\tbuffer->index_ = index;\n-\t\t}\n-\n-\t\tbuffer->mem_ = &stream->buffers()[buffer->index_];\n \t}\n \n \tint ret = request->prepare();\n@@ -923,13 +896,6 @@ int Camera::stop()\n */\n void Camera::requestComplete(Request *request)\n {\n-\tfor (auto it : request->buffers()) {\n-\t\tStream *stream = it.first;\n-\t\tBuffer *buffer = it.second;\n-\t\tif (stream->memoryType() == ExternalMemory)\n-\t\t\tstream->unmapBuffer(buffer);\n-\t}\n-\n \trequestCompleted.emit(request);\n \tdelete request;\n }\ndiff --git a/src/libcamera/include/pipeline_handler.h b/src/libcamera/include/pipeline_handler.h\nindex 6ce8ea1c8d31b870..e4588f10bac0228c 100644\n--- a/src/libcamera/include/pipeline_handler.h\n+++ b/src/libcamera/include/pipeline_handler.h\n@@ -20,12 +20,12 @@\n \n namespace libcamera {\n \n-class Buffer;\n class Camera;\n class CameraConfiguration;\n class CameraManager;\n class DeviceEnumerator;\n class DeviceMatch;\n+class FrameBuffer;\n class MediaDevice;\n class PipelineHandler;\n class Request;\n@@ -79,7 +79,8 @@ public:\n \n \tint queueRequest(Camera *camera, Request *request);\n \n-\tbool completeBuffer(Camera *camera, Request *request, Buffer *buffer);\n+\tbool completeBuffer(Camera *camera, Request *request,\n+\t\t\t FrameBuffer *buffer);\n \tvoid completeRequest(Camera *camera, Request *request);\n \n \tconst char *name() const { return name_; }\ndiff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\nindex 0e94c3f71d0b1208..ced3cb229c509ad2 100644\n--- a/src/libcamera/include/v4l2_videodevice.h\n+++ b/src/libcamera/include/v4l2_videodevice.h\n@@ -167,9 +167,8 @@ public:\n \tint externalBuffers(unsigned int count);\n \tint releaseBuffers();\n \n-\tint queueBuffer(Buffer *buffer);\n-\tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n-\tSignal<Buffer *> bufferReady;\n+\tint queueBuffer(FrameBuffer *buffer);\n+\tSignal<FrameBuffer *> bufferReady;\n \n \tint streamOn();\n \tint streamOff();\n@@ -203,7 +202,7 @@ private:\n \tFrameBuffer *createBuffer(struct v4l2_buffer buf);\n \tint exportDmaBuffer(unsigned int index, unsigned int plane);\n \n-\tBuffer *dequeueBuffer();\n+\tFrameBuffer *dequeueBuffer();\n \tvoid bufferAvailable(EventNotifier *notifier);\n \n \tV4L2Capability caps_;\n@@ -213,7 +212,7 @@ private:\n \n \tBufferPool *bufferPool_;\n \tV4L2BufferCache *cache_;\n-\tstd::map<unsigned int, Buffer *> queuedBuffers_;\n+\tstd::map<unsigned int, FrameBuffer *> queuedBuffers_;\n \n \tEventNotifier *fdEvent_;\n };\ndiff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\nindex 5080ac6c4d0abe3b..8d9420aea3eb0b21 100644\n--- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n@@ -31,6 +31,8 @@ namespace libcamera {\n \n LOG_DEFINE_CATEGORY(IPU3)\n \n+class IPU3CameraData;\n+\n class ImgUDevice\n {\n public:\n@@ -44,7 +46,7 @@ public:\n \t\tV4L2VideoDevice *dev;\n \t\tunsigned int pad;\n \t\tstd::string name;\n-\t\tBufferPool *pool;\n+\t\tstd::vector<FrameBuffer *> buffers;\n \t};\n \n \tImgUDevice()\n@@ -70,10 +72,9 @@ public:\n \tint configureOutput(ImgUOutput *output,\n \t\t\t const StreamConfiguration &cfg);\n \n-\tint importInputBuffers(BufferPool *pool);\n-\tint importOutputBuffers(ImgUOutput *output, BufferPool *pool);\n-\tint exportOutputBuffers(ImgUOutput *output, BufferPool *pool);\n-\tvoid freeBuffers();\n+\tint importInputBuffers(unsigned int count);\n+\tint exportOutputBuffers(ImgUOutput *output, unsigned int count);\n+\tvoid freeBuffers(IPU3CameraData *data);\n \n \tint start();\n \tint stop();\n@@ -93,10 +94,6 @@ public:\n \tImgUOutput viewfinder_;\n \tImgUOutput stat_;\n \t/* \\todo Add param video device for 3A tuning */\n-\n-\tBufferPool vfPool_;\n-\tBufferPool statPool_;\n-\tBufferPool outPool_;\n };\n \n class CIO2Device\n@@ -120,10 +117,10 @@ public:\n \tint configure(const Size &size,\n \t\t V4L2DeviceFormat *outputFormat);\n \n-\tBufferPool *exportBuffers();\n+\tint exportBuffers();\n \tvoid freeBuffers();\n \n-\tint start(std::vector<std::unique_ptr<Buffer>> *buffers);\n+\tint start();\n \tint stop();\n \n \tstatic int mediaBusToFormat(unsigned int code);\n@@ -132,7 +129,8 @@ public:\n \tV4L2Subdevice *csi2_;\n \tCameraSensor *sensor_;\n \n-\tBufferPool pool_;\n+private:\n+\tstd::vector<FrameBuffer *> buffers_;\n };\n \n class IPU3Stream : public V4L2Stream\n@@ -163,17 +161,15 @@ public:\n \t\tdelete vfStream_;\n \t}\n \n-\tvoid imguOutputBufferReady(Buffer *buffer);\n-\tvoid imguInputBufferReady(Buffer *buffer);\n-\tvoid cio2BufferReady(Buffer *buffer);\n+\tvoid imguOutputBufferReady(FrameBuffer *buffer);\n+\tvoid imguInputBufferReady(FrameBuffer *buffer);\n+\tvoid cio2BufferReady(FrameBuffer *buffer);\n \n \tCIO2Device cio2_;\n \tImgUDevice *imgu_;\n \n \tIPU3Stream *outStream_;\n \tIPU3Stream *vfStream_;\n-\n-\tstd::vector<std::unique_ptr<Buffer>> rawBuffers_;\n };\n \n class IPU3CameraConfiguration : public CameraConfiguration\n@@ -637,70 +633,42 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera,\n \t\t\t\t\t const std::set<Stream *> &streams)\n {\n \tIPU3CameraData *data = cameraData(camera);\n-\tIPU3Stream *outStream = data->outStream_;\n-\tIPU3Stream *vfStream = data->vfStream_;\n-\tCIO2Device *cio2 = &data->cio2_;\n-\tImgUDevice *imgu = data->imgu_;\n \tunsigned int bufferCount;\n \tint ret;\n \n-\t/* Share buffers between CIO2 output and ImgU input. */\n-\tBufferPool *pool = cio2->exportBuffers();\n-\tif (!pool)\n-\t\treturn -ENOMEM;\n+\tret = data->cio2_.exportBuffers();\n+\tif (ret < 0)\n+\t\treturn ret;\n \n-\tret = imgu->importInputBuffers(pool);\n-\tif (ret)\n-\t\tgoto error;\n+\tbufferCount = ret;\n \n-\t/*\n-\t * Use for the stat's internal pool the same number of buffer as\n-\t * for the input pool.\n-\t * \\todo To be revised when we'll actually use the stat node.\n-\t */\n-\tbufferCount = pool->count();\n-\timgu->stat_.pool->createBuffers(bufferCount);\n-\tret = imgu->exportOutputBuffers(&imgu->stat_, imgu->stat_.pool);\n+\tret = data->imgu_->importInputBuffers(bufferCount);\n \tif (ret)\n \t\tgoto error;\n \n-\t/* Allocate buffers for each active stream. */\n-\tfor (Stream *s : streams) {\n-\t\tIPU3Stream *stream = static_cast<IPU3Stream *>(s);\n-\t\tImgUDevice::ImgUOutput *dev = stream->device_;\n-\n-\t\tif (stream->memoryType() == InternalMemory)\n-\t\t\tret = imgu->exportOutputBuffers(dev, &stream->bufferPool());\n-\t\telse\n-\t\t\tret = imgu->importOutputBuffers(dev, &stream->bufferPool());\n-\t\tif (ret)\n-\t\t\tgoto error;\n-\t}\n+\tret = data->imgu_->exportOutputBuffers(&data->imgu_->stat_, bufferCount);\n+\tif (ret < 0)\n+\t\tgoto error;\n \n \t/*\n \t * Allocate buffers also on non-active outputs; use the same number\n \t * of buffers as the active ones.\n \t */\n-\tif (!outStream->active_) {\n-\t\tbufferCount = vfStream->configuration().bufferCount;\n-\t\toutStream->device_->pool->createBuffers(bufferCount);\n-\t\tret = imgu->exportOutputBuffers(outStream->device_,\n-\t\t\t\t\t\toutStream->device_->pool);\n-\t\tif (ret)\n+\tif (!data->outStream_->active_) {\n+\t\tret = data->imgu_->exportOutputBuffers(data->outStream_->device_,\n+\t\t\t\t\t\t bufferCount);\n+\t\tif (ret < 0)\n \t\t\tgoto error;\n \t}\n \n-\tif (!vfStream->active_) {\n-\t\tbufferCount = outStream->configuration().bufferCount;\n-\t\tvfStream->device_->pool->createBuffers(bufferCount);\n-\t\tret = imgu->exportOutputBuffers(vfStream->device_,\n-\t\t\t\t\t\tvfStream->device_->pool);\n-\t\tif (ret)\n+\tif (!data->vfStream_->active_) {\n+\t\tret = data->imgu_->exportOutputBuffers(data->vfStream_->device_,\n+\t\t\t\t\t\t bufferCount);\n+\t\tif (ret < 0)\n \t\t\tgoto error;\n \t}\n \n \treturn 0;\n-\n error:\n \tfreeBuffers(camera, streams);\n \n@@ -713,7 +681,7 @@ int PipelineHandlerIPU3::freeBuffers(Camera *camera,\n \tIPU3CameraData *data = cameraData(camera);\n \n \tdata->cio2_.freeBuffers();\n-\tdata->imgu_->freeBuffers();\n+\tdata->imgu_->freeBuffers(data);\n \n \treturn 0;\n }\n@@ -729,7 +697,7 @@ int PipelineHandlerIPU3::start(Camera *camera)\n \t * Start the ImgU video devices, buffers will be queued to the\n \t * ImgU output and viewfinder when requests will be queued.\n \t */\n-\tret = cio2->start(&data->rawBuffers_);\n+\tret = cio2->start();\n \tif (ret)\n \t\tgoto error;\n \n@@ -745,7 +713,6 @@ int PipelineHandlerIPU3::start(Camera *camera)\n error:\n \tLOG(IPU3, Error) << \"Failed to start camera \" << camera->name();\n \n-\tdata->rawBuffers_.clear();\n \treturn ret;\n }\n \n@@ -759,8 +726,6 @@ void PipelineHandlerIPU3::stop(Camera *camera)\n \tif (ret)\n \t\tLOG(IPU3, Warning) << \"Failed to stop camera \"\n \t\t\t\t << camera->name();\n-\n-\tdata->rawBuffers_.clear();\n }\n \n int PipelineHandlerIPU3::queueRequestHardware(Camera *camera, Request *request)\n@@ -769,7 +734,7 @@ int PipelineHandlerIPU3::queueRequestHardware(Camera *camera, Request *request)\n \n \tfor (auto it : request->buffers()) {\n \t\tIPU3Stream *stream = static_cast<IPU3Stream *>(it.first);\n-\t\tBuffer *buffer = it.second;\n+\t\tFrameBuffer *buffer = it.second;\n \n \t\tint ret = stream->device_->dev->queueBuffer(buffer);\n \t\tif (ret < 0)\n@@ -930,7 +895,7 @@ int PipelineHandlerIPU3::registerCameras()\n * Buffers completed from the ImgU input are immediately queued back to the\n * CIO2 unit to continue frame capture.\n */\n-void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n+void IPU3CameraData::imguInputBufferReady(FrameBuffer *buffer)\n {\n \t/* \\todo Handle buffer failures when state is set to BufferError. */\n \tif (buffer->info().status() == BufferInfo::BufferCancelled)\n@@ -945,7 +910,7 @@ void IPU3CameraData::imguInputBufferReady(Buffer *buffer)\n *\n * Buffers completed from the ImgU output are directed to the application.\n */\n-void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n+void IPU3CameraData::imguOutputBufferReady(FrameBuffer *buffer)\n {\n \tRequest *request = buffer->request();\n \n@@ -964,7 +929,7 @@ void IPU3CameraData::imguOutputBufferReady(Buffer *buffer)\n * Buffers completed from the CIO2 are immediately queued to the ImgU unit\n * for further processing.\n */\n-void IPU3CameraData::cio2BufferReady(Buffer *buffer)\n+void IPU3CameraData::cio2BufferReady(FrameBuffer *buffer)\n {\n \t/* \\todo Handle buffer failures when state is set to BufferError. */\n \tif (buffer->info().status() == BufferInfo::BufferCancelled)\n@@ -1020,7 +985,6 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index)\n \n \toutput_.pad = PAD_OUTPUT;\n \toutput_.name = \"output\";\n-\toutput_.pool = &outPool_;\n \n \tviewfinder_.dev = V4L2VideoDevice::fromEntityName(media,\n \t\t\t\t\t\t\t name_ + \" viewfinder\");\n@@ -1030,7 +994,6 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index)\n \n \tviewfinder_.pad = PAD_VF;\n \tviewfinder_.name = \"viewfinder\";\n-\tviewfinder_.pool = &vfPool_;\n \n \tstat_.dev = V4L2VideoDevice::fromEntityName(media, name_ + \" 3a stat\");\n \tret = stat_.dev->open();\n@@ -1039,7 +1002,6 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index)\n \n \tstat_.pad = PAD_STAT;\n \tstat_.name = \"stat\";\n-\tstat_.pool = &statPool_;\n \n \treturn 0;\n }\n@@ -1139,85 +1101,49 @@ int ImgUDevice::configureOutput(ImgUOutput *output,\n \treturn 0;\n }\n \n-/**\n- * \\brief Import buffers from \\a pool into the ImgU input\n- * \\param[in] pool The buffer pool to import\n- * \\return 0 on success or a negative error code otherwise\n- */\n-int ImgUDevice::importInputBuffers(BufferPool *pool)\n+int ImgUDevice::importInputBuffers(unsigned int count)\n {\n-\tint ret = input_->importBuffers(pool);\n-\tif (ret) {\n+\tint ret = input_->externalBuffers(count);\n+\tif (ret)\n \t\tLOG(IPU3, Error) << \"Failed to import ImgU input buffers\";\n-\t\treturn ret;\n-\t}\n \n-\treturn 0;\n+\treturn ret;\n }\n \n-/**\n- * \\brief Export buffers from \\a output to the provided \\a pool\n- * \\param[in] output The ImgU output device\n- * \\param[in] pool The buffer pool where to export buffers\n- *\n- * Export memory buffers reserved in the video device memory associated with\n- * \\a output id to the buffer pool provided as argument.\n- *\n- * \\return 0 on success or a negative error code otherwise\n- */\n-int ImgUDevice::exportOutputBuffers(ImgUOutput *output, BufferPool *pool)\n+int ImgUDevice::exportOutputBuffers(ImgUOutput *output, unsigned int count)\n {\n-\tint ret = output->dev->exportBuffers(pool);\n-\tif (ret) {\n-\t\tLOG(IPU3, Error) << \"Failed to export ImgU \"\n-\t\t\t\t << output->name << \" buffers\";\n-\t\treturn ret;\n-\t}\n-\n-\treturn 0;\n-}\n+\tint ret;\n \n-/**\n- * \\brief Reserve buffers in \\a output from the provided \\a pool\n- * \\param[in] output The ImgU output device\n- * \\param[in] pool The buffer pool used to reserve buffers in \\a output\n- *\n- * Reserve a number of buffers equal to the number of buffers in \\a pool\n- * in the \\a output device.\n- *\n- * \\return 0 on success or a negative error code otherwise\n- */\n-int ImgUDevice::importOutputBuffers(ImgUOutput *output, BufferPool *pool)\n-{\n-\tint ret = output->dev->importBuffers(pool);\n-\tif (ret) {\n-\t\tLOG(IPU3, Error)\n-\t\t\t<< \"Failed to import buffer in \" << output->name\n-\t\t\t<< \" ImgU device\";\n-\t\treturn ret;\n-\t}\n+\tret = output->dev->allocateBuffers(count, &output->buffers);\n+\tif (ret < 0)\n+\t\tLOG(IPU3, Error) << \"Failed to allocate ImgU \"\n+\t\t\t\t << output->name << \" buffers\";\n \n-\treturn 0;\n+\treturn ret;\n }\n \n /**\n * \\brief Release buffers for all the ImgU video devices\n */\n-void ImgUDevice::freeBuffers()\n+void ImgUDevice::freeBuffers(IPU3CameraData *data)\n {\n \tint ret;\n \n-\tret = output_.dev->releaseBuffers();\n-\tif (ret)\n-\t\tLOG(IPU3, Error) << \"Failed to release ImgU output buffers\";\n+\tif (!data->outStream_->active_) {\n+\t\tret = output_.dev->releaseBuffers();\n+\t\tif (ret)\n+\t\t\tLOG(IPU3, Error) << \"Failed to release ImgU output buffers\";\n+\t}\n \n \tret = stat_.dev->releaseBuffers();\n \tif (ret)\n \t\tLOG(IPU3, Error) << \"Failed to release ImgU stat buffers\";\n \n-\tret = viewfinder_.dev->releaseBuffers();\n-\tif (ret)\n-\t\tLOG(IPU3, Error) << \"Failed to release ImgU viewfinder buffers\";\n+\tif (!data->vfStream_->active_) {\n+\t\tret = viewfinder_.dev->releaseBuffers();\n+\t\tif (ret)\n+\t\t\tLOG(IPU3, Error) << \"Failed to release ImgU viewfinder buffers\";\n+\t}\n \n \tret = input_->releaseBuffers();\n \tif (ret)\n@@ -1452,38 +1378,33 @@ int CIO2Device::configure(const Size &size,\n \treturn 0;\n }\n \n-/**\n- * \\brief Allocate CIO2 memory buffers and export them in a BufferPool\n- *\n- * Allocate memory buffers in the CIO2 video device and export them to\n- * a buffer pool that can be imported by another device.\n- *\n- * \\return The buffer pool with export buffers on success or nullptr otherwise\n- */\n-BufferPool *CIO2Device::exportBuffers()\n+int CIO2Device::exportBuffers()\n {\n-\tpool_.createBuffers(CIO2_BUFFER_COUNT);\n-\n-\tint ret = output_->exportBuffers(&pool_);\n-\tif (ret) {\n+\tint ret = output_->allocateBuffers(CIO2_BUFFER_COUNT, &buffers_);\n+\tif (ret < 0)\n \t\tLOG(IPU3, Error) << \"Failed to export CIO2 buffers\";\n-\t\treturn nullptr;\n-\t}\n \n-\treturn &pool_;\n+\treturn ret;\n }\n \n void CIO2Device::freeBuffers()\n {\n+\tfor (FrameBuffer *buffer : buffers_)\n+\t\tdelete buffer;\n+\n \tif (output_->releaseBuffers())\n \t\tLOG(IPU3, Error) << \"Failed to release CIO2 buffers\";\n }\n \n-int CIO2Device::start(std::vector<std::unique_ptr<Buffer>> *buffers)\n+int CIO2Device::start()\n {\n-\t*buffers = output_->queueAllBuffers();\n-\tif (buffers->empty())\n-\t\treturn -EINVAL;\n+\tfor (FrameBuffer *buffer : buffers_) {\n+\t\tint ret = output_->queueBuffer(buffer);\n+\t\tif (ret) {\n+\t\t\tLOG(IPU3, Error) << \"Failed to queue CIO2 buffer\";\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n \n \treturn output_->streamOn();\n }\ndiff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\nindex ca3d92c7ad637c3a..96e863f0208fa748 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -32,8 +32,7 @@\n #include \"v4l2_subdevice.h\"\n #include \"v4l2_videodevice.h\"\n \n-#define RKISP1_PARAM_BASE 0x100\n-#define RKISP1_STAT_BASE 0x200\n+#define RKISP1_BUFFER_COUNT 4\n \n namespace libcamera {\n \n@@ -52,9 +51,9 @@ struct RkISP1FrameInfo {\n \tunsigned int frame;\n \tRequest *request;\n \n-\tBuffer *paramBuffer;\n-\tBuffer *statBuffer;\n-\tBuffer *videoBuffer;\n+\tFrameBuffer *paramBuffer;\n+\tFrameBuffer *statBuffer;\n+\tFrameBuffer *videoBuffer;\n \n \tbool paramFilled;\n \tbool paramDequeued;\n@@ -70,7 +69,7 @@ public:\n \tint destroy(unsigned int frame);\n \n \tRkISP1FrameInfo *find(unsigned int frame);\n-\tRkISP1FrameInfo *find(Buffer *buffer);\n+\tRkISP1FrameInfo *find(FrameBuffer *buffer);\n \tRkISP1FrameInfo *find(Request *request);\n \n private:\n@@ -89,7 +88,7 @@ public:\n \t\tsetDelay(QueueBuffers, -1, 10);\n \t}\n \n-\tvoid bufferReady(Buffer *buffer)\n+\tvoid bufferReady(FrameBuffer *buffer)\n \t{\n \t\t/*\n \t\t * Calculate SOE by taking the end of DMA set by the kernel and applying\n@@ -154,8 +153,6 @@ public:\n \tconst V4L2SubdeviceFormat &sensorFormat() { return sensorFormat_; }\n \n private:\n-\tstatic constexpr unsigned int RKISP1_BUFFER_COUNT = 4;\n-\n \t/*\n \t * The RkISP1CameraData instance is guaranteed to be valid as long as the\n \t * corresponding Camera instance is valid. In order to borrow a\n@@ -203,9 +200,9 @@ private:\n \tint initLinks();\n \tint createCamera(MediaEntity *sensor);\n \tvoid tryCompleteRequest(Request *request);\n-\tvoid bufferReady(Buffer *buffer);\n-\tvoid paramReady(Buffer *buffer);\n-\tvoid statReady(Buffer *buffer);\n+\tvoid bufferReady(FrameBuffer *buffer);\n+\tvoid paramReady(FrameBuffer *buffer);\n+\tvoid statReady(FrameBuffer *buffer);\n \n \tMediaDevice *media_;\n \tV4L2Subdevice *dphy_;\n@@ -214,11 +211,8 @@ private:\n \tV4L2VideoDevice *param_;\n \tV4L2VideoDevice *stat_;\n \n-\tBufferPool paramPool_;\n-\tBufferPool statPool_;\n-\n-\tstd::queue<Buffer *> paramBuffers_;\n-\tstd::queue<Buffer *> statBuffers_;\n+\tstd::queue<FrameBuffer *> paramBuffers_;\n+\tstd::queue<FrameBuffer *> statBuffers_;\n \n \tCamera *activeCamera_;\n };\n@@ -234,15 +228,15 @@ RkISP1FrameInfo *RkISP1Frames::create(unsigned int frame, Request *request, Stre\n \t\tLOG(RkISP1, Error) << \"Parameters buffer underrun\";\n \t\treturn nullptr;\n \t}\n-\tBuffer *paramBuffer = pipe_->paramBuffers_.front();\n+\tFrameBuffer *paramBuffer = pipe_->paramBuffers_.front();\n \n \tif (pipe_->statBuffers_.empty()) {\n \t\tLOG(RkISP1, Error) << \"Statisitc buffer underrun\";\n \t\treturn nullptr;\n \t}\n-\tBuffer *statBuffer = pipe_->statBuffers_.front();\n+\tFrameBuffer *statBuffer = pipe_->statBuffers_.front();\n \n-\tBuffer *videoBuffer = request->findBuffer(stream);\n+\tFrameBuffer *videoBuffer = request->findBuffer(stream);\n \tif (!videoBuffer) {\n \t\tLOG(RkISP1, Error)\n \t\t\t<< \"Attempt to queue request with invalid stream\";\n@@ -295,7 +289,7 @@ RkISP1FrameInfo *RkISP1Frames::find(unsigned int frame)\n \treturn nullptr;\n }\n \n-RkISP1FrameInfo *RkISP1Frames::find(Buffer *buffer)\n+RkISP1FrameInfo *RkISP1Frames::find(FrameBuffer *buffer)\n {\n \tfor (auto &itInfo : frameInfo_) {\n \t\tRkISP1FrameInfo *info = itInfo.second;\n@@ -661,57 +655,42 @@ int PipelineHandlerRkISP1::allocateBuffers(Camera *camera,\n \t\t\t\t\t const std::set<Stream *> &streams)\n {\n \tRkISP1CameraData *data = cameraData(camera);\n-\tStream *stream = *streams.begin();\n+\tstd::vector<FrameBuffer *> paramBuffers, statBuffers;\n+\tunsigned int count = 1;\n \tint ret;\n \n-\tif (stream->memoryType() == InternalMemory)\n-\t\tret = video_->exportBuffers(&stream->bufferPool());\n-\telse\n-\t\tret = video_->importBuffers(&stream->bufferPool());\n \n-\tif (ret)\n-\t\treturn ret;\n+\tret = param_->allocateBuffers(RKISP1_BUFFER_COUNT, ¶mBuffers);\n+\tif (ret < 0)\n+\t\tgoto err;\n \n-\tparamPool_.createBuffers(stream->configuration().bufferCount + 1);\n-\tret = param_->exportBuffers(¶mPool_);\n-\tif (ret) {\n-\t\tvideo_->releaseBuffers();\n-\t\treturn ret;\n-\t}\n+\tret = stat_->allocateBuffers(RKISP1_BUFFER_COUNT, &statBuffers);\n+\tif (ret < 0)\n+\t\tgoto err;\n \n-\tstatPool_.createBuffers(stream->configuration().bufferCount + 1);\n-\tret = stat_->exportBuffers(&statPool_);\n-\tif (ret) {\n-\t\tparam_->releaseBuffers();\n-\t\tvideo_->releaseBuffers();\n-\t\treturn ret;\n+\tfor (FrameBuffer *buffer : paramBuffers) {\n+\t\tbuffer->setCookie(count++);\n+\t\tdata->ipaBuffers_.push_back({ .id = buffer->cookie(),\n+\t\t\t\t\t .planes = buffer->planes() });\n+\t\tparamBuffers_.push(buffer);\n \t}\n \n-\tfor (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) {\n-\t\tstd::vector<FrameBuffer::Plane> planes;\n-\t\tplanes.push_back({\n-\t\t\t.fd = paramPool_.buffers()[i].planes()[0].fd(),\n-\t\t\t.length = paramPool_.buffers()[i].planes()[0].length(),\n-\t\t});\n-\n-\t\tdata->ipaBuffers_.push_back({ .id = RKISP1_PARAM_BASE | i,\n-\t\t\t\t\t .planes = planes });\n-\t\tparamBuffers_.push(new Buffer(i));\n+\tfor (FrameBuffer *buffer : statBuffers) {\n+\t\tbuffer->setCookie(count++);\n+\t\tdata->ipaBuffers_.push_back({ .id = buffer->cookie(),\n+\t\t\t\t\t .planes = buffer->planes() });\n+\t\tstatBuffers_.push(buffer);\n \t}\n \n-\tfor (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) {\n-\t\tstd::vector<FrameBuffer::Plane> planes;\n-\t\tplanes.push_back({\n-\t\t\t.fd = statPool_.buffers()[i].planes()[0].fd(),\n-\t\t\t.length = statPool_.buffers()[i].planes()[0].length(),\n-\t\t});\n+\tdata->ipa_->mapBuffers(data->ipaBuffers_);\n \n-\t\tdata->ipaBuffers_.push_back({ .id = RKISP1_STAT_BASE | i,\n-\t\t\t\t\t .planes = planes });\n-\t\tstatBuffers_.push(new Buffer(i));\n-\t}\n+\treturn 0;\n+err:\n+\tfor (FrameBuffer *buffer : paramBuffers)\n+\t\tdelete buffer;\n \n-\tdata->ipa_->mapBuffers(data->ipaBuffers_);\n+\tfor (FrameBuffer *buffer : statBuffers)\n+\t\tdelete buffer;\n \n \treturn ret;\n }\n@@ -744,9 +723,6 @@ int PipelineHandlerRkISP1::freeBuffers(Camera *camera,\n \tif (stat_->releaseBuffers())\n \t\tLOG(RkISP1, Error) << \"Failed to release stat buffers\";\n \n-\tif (video_->releaseBuffers())\n-\t\tLOG(RkISP1, Error) << \"Failed to release video buffers\";\n-\n \treturn 0;\n }\n \n@@ -834,9 +810,11 @@ int PipelineHandlerRkISP1::queueRequestHardware(Camera *camera,\n \tif (!info)\n \t\treturn -ENOENT;\n \n+\tunsigned int paramid = info->paramBuffer->cookie();\n+\n \tIPAOperationData op;\n \top.operation = RKISP1_IPA_EVENT_QUEUE_REQUEST;\n-\top.data = { data->frame_, RKISP1_PARAM_BASE | info->paramBuffer->index() };\n+\top.data = { data->frame_, paramid };\n \top.controls = { request->controls() };\n \tdata->ipa_->processEvent(op);\n \n@@ -996,12 +974,12 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\n \tif (!info->paramDequeued)\n \t\treturn;\n \n-\tcompleteRequest(activeCamera_, request);\n-\n \tdata->frameInfo_.destroy(info->frame);\n+\n+\tcompleteRequest(activeCamera_, request);\n }\n \n-void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n+void PipelineHandlerRkISP1::bufferReady(FrameBuffer *buffer)\n {\n \tASSERT(activeCamera_);\n \tRkISP1CameraData *data = cameraData(activeCamera_);\n@@ -1016,7 +994,7 @@ void PipelineHandlerRkISP1::bufferReady(Buffer *buffer)\n \ttryCompleteRequest(request);\n }\n \n-void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n+void PipelineHandlerRkISP1::paramReady(FrameBuffer *buffer)\n {\n \tASSERT(activeCamera_);\n \tRkISP1CameraData *data = cameraData(activeCamera_);\n@@ -1027,7 +1005,7 @@ void PipelineHandlerRkISP1::paramReady(Buffer *buffer)\n \ttryCompleteRequest(info->request);\n }\n \n-void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n+void PipelineHandlerRkISP1::statReady(FrameBuffer *buffer)\n {\n \tASSERT(activeCamera_);\n \tRkISP1CameraData *data = cameraData(activeCamera_);\n@@ -1037,7 +1015,7 @@ void PipelineHandlerRkISP1::statReady(Buffer *buffer)\n \t\treturn;\n \n \tunsigned int frame = info->frame;\n-\tunsigned int statid = RKISP1_STAT_BASE | info->statBuffer->index();\n+\tunsigned int statid = info->statBuffer->cookie();\n \n \tIPAOperationData op;\n \top.operation = RKISP1_IPA_EVENT_SIGNAL_STAT_BUFFER;\ndiff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\nindex 9cc90bf454cb4392..c275b9f0f75429bd 100644\n--- a/src/libcamera/pipeline/uvcvideo.cpp\n+++ b/src/libcamera/pipeline/uvcvideo.cpp\n@@ -42,7 +42,7 @@ public:\n \t}\n \n \tint init(MediaEntity *entity);\n-\tvoid bufferReady(Buffer *buffer);\n+\tvoid bufferReady(FrameBuffer *buffer);\n \n \tV4L2VideoDevice *video_;\n \tStream *stream_;\n@@ -196,23 +196,13 @@ int PipelineHandlerUVC::configure(Camera *camera, CameraConfiguration *config)\n int PipelineHandlerUVC::allocateBuffers(Camera *camera,\n \t\t\t\t\tconst std::set<Stream *> &streams)\n {\n-\tUVCCameraData *data = cameraData(camera);\n-\tStream *stream = *streams.begin();\n-\tconst StreamConfiguration &cfg = stream->configuration();\n-\n-\tLOG(UVC, Debug) << \"Requesting \" << cfg.bufferCount << \" buffers\";\n-\n-\tif (stream->memoryType() == InternalMemory)\n-\t\treturn data->video_->exportBuffers(&stream->bufferPool());\n-\telse\n-\t\treturn data->video_->importBuffers(&stream->bufferPool());\n+\treturn 0;\n }\n \n int PipelineHandlerUVC::freeBuffers(Camera *camera,\n \t\t\t\t const std::set<Stream *> &streams)\n {\n-\tUVCCameraData *data = cameraData(camera);\n-\treturn data->video_->releaseBuffers();\n+\treturn 0;\n }\n \n int PipelineHandlerUVC::start(Camera *camera)\n@@ -266,7 +256,7 @@ int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)\n int PipelineHandlerUVC::queueRequestHardware(Camera *camera, Request *request)\n {\n \tUVCCameraData *data = cameraData(camera);\n-\tBuffer *buffer = request->findBuffer(data->stream_);\n+\tFrameBuffer *buffer = request->findBuffer(data->stream_);\n \tif (!buffer) {\n \t\tLOG(UVC, Error)\n \t\t\t<< \"Attempt to queue request with invalid stream\";\n@@ -373,7 +363,7 @@ int UVCCameraData::init(MediaEntity *entity)\n \treturn 0;\n }\n \n-void UVCCameraData::bufferReady(Buffer *buffer)\n+void UVCCameraData::bufferReady(FrameBuffer *buffer)\n {\n \tRequest *request = buffer->request();\n \ndiff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\nindex b1222bd21fb629a0..64793788c752c791 100644\n--- a/src/libcamera/pipeline/vimc.cpp\n+++ b/src/libcamera/pipeline/vimc.cpp\n@@ -57,7 +57,7 @@ public:\n \t}\n \n \tint init(MediaDevice *media);\n-\tvoid bufferReady(Buffer *buffer);\n+\tvoid bufferReady(FrameBuffer *buffer);\n \n \tCameraSensor *sensor_;\n \tV4L2Subdevice *debayer_;\n@@ -264,23 +264,13 @@ int PipelineHandlerVimc::configure(Camera *camera, CameraConfiguration *config)\n int PipelineHandlerVimc::allocateBuffers(Camera *camera,\n \t\t\t\t\t const std::set<Stream *> &streams)\n {\n-\tVimcCameraData *data = cameraData(camera);\n-\tStream *stream = *streams.begin();\n-\tconst StreamConfiguration &cfg = stream->configuration();\n-\n-\tLOG(VIMC, Debug) << \"Requesting \" << cfg.bufferCount << \" buffers\";\n-\n-\tif (stream->memoryType() == InternalMemory)\n-\t\treturn data->video_->exportBuffers(&stream->bufferPool());\n-\telse\n-\t\treturn data->video_->importBuffers(&stream->bufferPool());\n+\treturn 0;\n }\n \n int PipelineHandlerVimc::freeBuffers(Camera *camera,\n \t\t\t\t const std::set<Stream *> &streams)\n {\n-\tVimcCameraData *data = cameraData(camera);\n-\treturn data->video_->releaseBuffers();\n+\treturn 0;\n }\n \n int PipelineHandlerVimc::start(Camera *camera)\n@@ -328,7 +318,7 @@ int PipelineHandlerVimc::processControls(VimcCameraData *data, Request *request)\n int PipelineHandlerVimc::queueRequestHardware(Camera *camera, Request *request)\n {\n \tVimcCameraData *data = cameraData(camera);\n-\tBuffer *buffer = request->findBuffer(data->stream_);\n+\tFrameBuffer *buffer = request->findBuffer(data->stream_);\n \tif (!buffer) {\n \t\tLOG(VIMC, Error)\n \t\t\t<< \"Attempt to queue request with invalid stream\";\n@@ -459,7 +449,7 @@ int VimcCameraData::init(MediaDevice *media)\n \treturn 0;\n }\n \n-void VimcCameraData::bufferReady(Buffer *buffer)\n+void VimcCameraData::bufferReady(FrameBuffer *buffer)\n {\n \tRequest *request = buffer->request();\n \ndiff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\nindex c9e348b98da7b736..b107e23258dee692 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -398,7 +398,7 @@ int PipelineHandler::queueRequest(Camera *camera, Request *request)\n * otherwise\n */\n bool PipelineHandler::completeBuffer(Camera *camera, Request *request,\n-\t\t\t\t Buffer *buffer)\n+\t\t\t\t FrameBuffer *buffer)\n {\n \tcamera->bufferCompleted.emit(request, buffer);\n \treturn request->completeBuffer(buffer);\ndiff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\nindex 7593bf9dfa546401..a4b27e1cb08a7641 100644\n--- a/src/libcamera/request.cpp\n+++ b/src/libcamera/request.cpp\n@@ -75,11 +75,6 @@ Request::Request(Camera *camera, uint64_t cookie)\n \n Request::~Request()\n {\n-\tfor (auto it : bufferMap_) {\n-\t\tBuffer *buffer = it.second;\n-\t\tdelete buffer;\n-\t}\n-\n \tdelete metadata_;\n \tdelete controls_;\n \tdelete validator_;\n@@ -126,7 +121,7 @@ Request::~Request()\n * \\retval -EEXIST The request already contains a buffer for the stream\n * \\retval -EINVAL The buffer does not reference a valid Stream\n */\n-int Request::addBuffer(Stream *stream, std::unique_ptr<Buffer> buffer)\n+int Request::addBuffer(Stream *stream, FrameBuffer *buffer)\n {\n \tif (!stream) {\n \t\tLOG(Request, Error) << \"Invalid stream reference\";\n@@ -139,7 +134,7 @@ int Request::addBuffer(Stream *stream, std::unique_ptr<Buffer> buffer)\n \t\treturn -EEXIST;\n \t}\n \n-\tbufferMap_[stream] = buffer.release();\n+\tbufferMap_[stream] = buffer;\n \n \treturn 0;\n }\n@@ -159,7 +154,7 @@ int Request::addBuffer(Stream *stream, std::unique_ptr<Buffer> buffer)\n * \\return The buffer associated with the stream, or nullptr if the stream is\n * not part of this request\n */\n-Buffer *Request::findBuffer(Stream *stream) const\n+FrameBuffer *Request::findBuffer(Stream *stream) const\n {\n \tauto it = bufferMap_.find(stream);\n \tif (it == bufferMap_.end())\n@@ -219,8 +214,9 @@ int Request::prepare()\n \t}\n \n \tfor (auto const &pair : bufferMap_) {\n-\t\tBuffer *buffer = pair.second;\n+\t\tFrameBuffer *buffer = pair.second;\n \t\tbuffer->request_ = this;\n+\n \t\tpending_.insert(buffer);\n \t}\n \n@@ -253,7 +249,7 @@ void Request::complete()\n * \\return True if all buffers contained in the request have completed, false\n * otherwise\n */\n-bool Request::completeBuffer(Buffer *buffer)\n+bool Request::completeBuffer(FrameBuffer *buffer)\n {\n \tint ret = pending_.erase(buffer);\n \tASSERT(ret == 1);\ndiff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\nindex f5810956b2040ce6..a29ed697468a7f59 100644\n--- a/src/libcamera/v4l2_videodevice.cpp\n+++ b/src/libcamera/v4l2_videodevice.cpp\n@@ -1191,9 +1191,11 @@ int V4L2VideoDevice::externalBuffers(unsigned int count)\n */\n int V4L2VideoDevice::releaseBuffers()\n {\n-\tLOG(V4L2, Debug) << \"Releasing bufferPool\";\n+\tLOG(V4L2, Debug) << \"Releasing buffers\";\n \n \tbufferPool_ = nullptr;\n+\tdelete cache_;\n+\tcache_ = nullptr;\n \n \treturn requestBuffers(0);\n }\n@@ -1209,27 +1211,26 @@ int V4L2VideoDevice::releaseBuffers()\n *\n * \\return 0 on success or a negative error code otherwise\n */\n-int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n+int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer)\n {\n \tstruct v4l2_plane v4l2Planes[VIDEO_MAX_PLANES] = {};\n \tstruct v4l2_buffer buf = {};\n \tint ret;\n \n-\tbuf.index = buffer->index();\n+\tbuf.index = cache_->fetch(buffer);\n \tbuf.type = bufferType_;\n \tbuf.memory = memoryType_;\n \tbuf.field = V4L2_FIELD_NONE;\n \n \tbool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type);\n-\tBufferMemory *mem = &bufferPool_->buffers()[buf.index];\n-\tconst std::vector<Dmabuf> &dmabufs = mem->planes();\n+\tconst std::vector<Dmabuf *> &dmabufs = buffer->dmabufs();\n \n \tif (buf.memory == V4L2_MEMORY_DMABUF) {\n \t\tif (multiPlanar) {\n \t\t\tfor (unsigned int p = 0; p < dmabufs.size(); ++p)\n-\t\t\t\tv4l2Planes[p].m.fd = dmabufs[p].fd();\n+\t\t\t\tv4l2Planes[p].m.fd = dmabufs[p]->fd();\n \t\t} else {\n-\t\t\tbuf.m.fd = dmabufs[0].fd();\n+\t\t\tbuf.m.fd = dmabufs[0]->fd();\n \t\t}\n \t}\n \n@@ -1275,52 +1276,6 @@ int V4L2VideoDevice::queueBuffer(Buffer *buffer)\n \treturn 0;\n }\n \n-/**\n- * \\brief Queue all buffers into the video device\n- *\n- * When starting video capture users of the video device often need to queue\n- * all allocated buffers to the device. This helper method simplifies the\n- * implementation of the user by queuing all buffers and returning a vector of\n- * Buffer instances for each queued buffer.\n- *\n- * This method is meant to be used with video capture devices internal to a\n- * pipeline handler, such as ISP statistics capture devices, or raw CSI-2\n- * receivers. For video capture devices facing applications, buffers shall\n- * instead be queued when requests are received, and for video output devices,\n- * buffers shall be queued when frames are ready to be output.\n- *\n- * The caller shall ensure that the returned buffers vector remains valid until\n- * all the queued buffers are dequeued, either during capture, or by stopping\n- * the video device.\n- *\n- * Calling this method on an output device or on a device that has buffers\n- * already queued is an error and will return an empty vector.\n- *\n- * \\return A vector of queued buffers, which will be empty if an error occurs\n- */\n-std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n-{\n-\tint ret;\n-\n-\tif (!queuedBuffers_.empty())\n-\t\treturn {};\n-\n-\tif (V4L2_TYPE_IS_OUTPUT(bufferType_))\n-\t\treturn {};\n-\n-\tstd::vector<std::unique_ptr<Buffer>> buffers;\n-\n-\tfor (unsigned int i = 0; i < bufferPool_->count(); ++i) {\n-\t\tBuffer *buffer = new Buffer(i);\n-\t\tbuffers.emplace_back(buffer);\n-\t\tret = queueBuffer(buffer);\n-\t\tif (ret)\n-\t\t\treturn {};\n-\t}\n-\n-\treturn buffers;\n-}\n-\n /**\n * \\brief Dequeue the next available buffer from the video device\n *\n@@ -1329,7 +1284,7 @@ std::vector<std::unique_ptr<Buffer>> V4L2VideoDevice::queueAllBuffers()\n *\n * \\return A pointer to the dequeued buffer on success, or nullptr otherwise\n */\n-Buffer *V4L2VideoDevice::dequeueBuffer()\n+FrameBuffer *V4L2VideoDevice::dequeueBuffer()\n {\n \tstruct v4l2_buffer buf = {};\n \tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n@@ -1352,15 +1307,15 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n \n \tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n \n+\tcache_->put(buf.index);\n+\n \tauto it = queuedBuffers_.find(buf.index);\n-\tBuffer *buffer = it->second;\n+\tFrameBuffer *buffer = it->second;\n \tqueuedBuffers_.erase(it);\n \n \tif (queuedBuffers_.empty())\n \t\tfdEvent_->setEnabled(false);\n \n-\tbuffer->index_ = buf.index;\n-\n \tBufferInfo::Status status = buf.flags & V4L2_BUF_FLAG_ERROR\n \t\t? BufferInfo::BufferError : BufferInfo::BufferSuccess;\n \tuint64_t timestamp = buf.timestamp.tv_sec * 1000000000ULL\n@@ -1383,7 +1338,7 @@ Buffer *V4L2VideoDevice::dequeueBuffer()\n */\n void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n {\n-\tBuffer *buffer = dequeueBuffer();\n+\tFrameBuffer *buffer = dequeueBuffer();\n \tif (!buffer)\n \t\treturn;\n \n@@ -1418,7 +1373,7 @@ int V4L2VideoDevice::streamOn()\n * \\brief Stop the video stream\n *\n * Buffers that are still queued when the video stream is stopped are\n- * immediately dequeued with their status set to Buffer::BufferError,\n+ * immediately dequeued with their status set to BufferInfo::BufferCancelled,\n * and the bufferReady signal is emitted for them. The order in which those\n * buffers are dequeued is not specified.\n *\n@@ -1437,11 +1392,10 @@ int V4L2VideoDevice::streamOff()\n \n \t/* Send back all queued buffers. */\n \tfor (auto it : queuedBuffers_) {\n-\t\tunsigned int index = it.first;\n-\t\tBuffer *buffer = it.second;\n+\t\tFrameBuffer *buffer = it.second;\n+\n+\t\tbuffer->info_.update(BufferInfo::BufferCancelled, 0, 0, { {} });\n \n-\t\tbuffer->index_ = index;\n-\t\tbuffer->cancel();\n \t\tbufferReady.emit(buffer);\n \t}\n \ndiff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\nindex 771020112f09b1ef..9650bd970f89aa41 100644\n--- a/src/qcam/main_window.cpp\n+++ b/src/qcam/main_window.cpp\n@@ -22,7 +22,7 @@\n using namespace libcamera;\n \n MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n-\t: options_(options), isCapturing_(false)\n+\t: options_(options), allocator_(nullptr), isCapturing_(false)\n {\n \tint ret;\n \n@@ -36,8 +36,10 @@ MainWindow::MainWindow(CameraManager *cm, const OptionsParser::Options &options)\n \tadjustSize();\n \n \tret = openCamera(cm);\n-\tif (!ret)\n+\tif (!ret) {\n+\t\tallocator_ = new BufferAllocator(camera_);\n \t\tret = startCapture();\n+\t}\n \n \tif (ret < 0)\n \t\tQTimer::singleShot(0, QCoreApplication::instance(),\n@@ -51,6 +53,8 @@ MainWindow::~MainWindow()\n \t\tcamera_->release();\n \t\tcamera_.reset();\n \t}\n+\n+\tdelete allocator_;\n }\n \n void MainWindow::updateTitle()\n@@ -175,8 +179,14 @@ int MainWindow::startCapture()\n \t\treturn ret;\n \t}\n \n+\tret = allocator_->allocate(stream, cfg);\n+\tif (ret < 0) {\n+\t\tstd::cerr << \"Failed to allocate internal buffers\" << std::endl;\n+\t\treturn ret;\n+\t}\n+\n \tstd::vector<Request *> requests;\n-\tfor (unsigned int i = 0; i < cfg.bufferCount; ++i) {\n+\tfor (FrameBuffer *buffer : allocator_->buffers(stream)) {\n \t\tRequest *request = camera_->createRequest();\n \t\tif (!request) {\n \t\t\tstd::cerr << \"Can't create request\" << std::endl;\n@@ -184,13 +194,7 @@ int MainWindow::startCapture()\n \t\t\tgoto error;\n \t\t}\n \n-\t\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(i);\n-\t\tif (!buffer) {\n-\t\t\tstd::cerr << \"Can't create buffer \" << i << std::endl;\n-\t\t\tgoto error;\n-\t\t}\n-\n-\t\tret = request->addBuffer(stream, std::move(buffer));\n+\t\tret = request->addBuffer(stream, buffer);\n \t\tif (ret < 0) {\n \t\t\tstd::cerr << \"Can't set buffer for request\" << std::endl;\n \t\t\tgoto error;\n@@ -253,11 +257,11 @@ void MainWindow::requestComplete(Request *request)\n \tif (request->status() == Request::RequestCancelled)\n \t\treturn;\n \n-\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n+\tconst std::map<Stream *, FrameBuffer *> &buffers = request->buffers();\n \n \tframesCaptured_++;\n \n-\tBuffer *buffer = buffers.begin()->second;\n+\tFrameBuffer *buffer = buffers.begin()->second;\n \tconst BufferInfo &info = buffer->info();\n \n \tdouble fps = info.timestamp() - lastBufferTime_;\n@@ -280,29 +284,20 @@ void MainWindow::requestComplete(Request *request)\n \n \tfor (auto it = buffers.begin(); it != buffers.end(); ++it) {\n \t\tStream *stream = it->first;\n-\t\tBuffer *buffer = it->second;\n-\t\tunsigned int index = buffer->index();\n-\n-\t\tstd::unique_ptr<Buffer> newBuffer = stream->createBuffer(index);\n-\t\tif (!newBuffer) {\n-\t\t\tstd::cerr << \"Can't create buffer\" << std::endl;\n-\t\t\treturn;\n-\t\t}\n+\t\tFrameBuffer *buffer = it->second;\n \n-\t\trequest->addBuffer(stream, std::move(newBuffer));\n+\t\trequest->addBuffer(stream, buffer);\n \t}\n \n \tcamera_->queueRequest(request);\n }\n \n-int MainWindow::display(Buffer *buffer)\n+int MainWindow::display(FrameBuffer *buffer)\n {\n-\tBufferMemory *mem = buffer->mem();\n-\tif (mem->planes().size() != 1)\n+\tif (buffer->planes().size() != 1)\n \t\treturn -EINVAL;\n \n-\tDmabuf &dmabuf = mem->planes().front();\n-\tunsigned char *raw = static_cast<unsigned char *>(dmabuf.mem());\n+\tunsigned char *raw = static_cast<unsigned char *>(buffer->dmabufs()[0]->mem());\n \tviewfinder_->display(raw, buffer->info().planes()[0].bytesused);\n \n \treturn 0;\ndiff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\nindex 0786e915ec512255..b38aa2ac959f44aa 100644\n--- a/src/qcam/main_window.h\n+++ b/src/qcam/main_window.h\n@@ -14,6 +14,8 @@\n #include <QObject>\n #include <QTimer>\n \n+#include <libcamera/allocator.h>\n+#include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n #include <libcamera/camera_manager.h>\n #include <libcamera/stream.h>\n@@ -49,7 +51,7 @@ private:\n \tvoid stopCapture();\n \n \tvoid requestComplete(Request *request);\n-\tint display(Buffer *buffer);\n+\tint display(FrameBuffer *buffer);\n \n \tQString title_;\n \tQTimer titleTimer_;\n@@ -57,6 +59,8 @@ private:\n \tconst OptionsParser::Options &options_;\n \n \tstd::shared_ptr<Camera> camera_;\n+\tBufferAllocator *allocator_;\n+\n \tbool isCapturing_;\n \tstd::unique_ptr<CameraConfiguration> config_;\n \ndiff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp\nindex 5dbaed9255d3d60c..c98afeb9597e21c2 100644\n--- a/test/camera/buffer_import.cpp\n+++ b/test/camera/buffer_import.cpp\n@@ -21,35 +21,48 @@\n #include \"test.h\"\n \n using namespace libcamera;\n+using namespace std;\n \n-/* Keep SINK_BUFFER_COUNT > CAMERA_BUFFER_COUNT + 1 */\n-static constexpr unsigned int SINK_BUFFER_COUNT = 8;\n-static constexpr unsigned int CAMERA_BUFFER_COUNT = 4;\n+namespace {\n \n-class FrameSink\n+class BufferSource\n {\n public:\n-\tFrameSink()\n+\tBufferSource()\n \t\t: video_(nullptr)\n \t{\n \t}\n \n-\tint init()\n+\t~BufferSource()\n+\t{\n+\t\tif (video_) {\n+\t\t\tvideo_->releaseBuffers();\n+\t\t\tvideo_->close();\n+\t\t}\n+\n+\t\tdelete video_;\n+\t\tvideo_ = nullptr;\n+\n+\t\tif (media_)\n+\t\t\tmedia_->release();\n+\t}\n+\n+\tint allocate(const StreamConfiguration &config)\n \t{\n \t\tint ret;\n \n \t\t/* Locate and open the video device. */\n-\t\tstd::string videoDeviceName = \"vivid-000-vid-out\";\n+\t\tstring videoDeviceName = \"vivid-000-vid-out\";\n \n-\t\tstd::unique_ptr<DeviceEnumerator> enumerator =\n+\t\tunique_ptr<DeviceEnumerator> enumerator =\n \t\t\tDeviceEnumerator::create();\n \t\tif (!enumerator) {\n-\t\t\tstd::cout << \"Failed to create device enumerator\" << std::endl;\n+\t\t\tcout << \"Failed to create device enumerator\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n \t\tif (enumerator->enumerate()) {\n-\t\t\tstd::cout << \"Failed to enumerate media devices\" << std::endl;\n+\t\t\tcout << \"Failed to enumerate media devices\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n@@ -58,13 +71,13 @@ public:\n \n \t\tmedia_ = enumerator->search(dm);\n \t\tif (!media_) {\n-\t\t\tstd::cout << \"No vivid output device available\" << std::endl;\n+\t\t\tcout << \"No vivid output device available\" << std::endl;\n \t\t\treturn TestSkip;\n \t\t}\n \n \t\tvideo_ = V4L2VideoDevice::fromEntityName(media_.get(), videoDeviceName);\n \t\tif (!video_) {\n-\t\t\tstd::cout << \"Unable to open \" << videoDeviceName << std::endl;\n+\t\t\tcout << \"Unable to open \" << videoDeviceName << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n@@ -72,366 +85,178 @@ public:\n \t\t\treturn TestFail;\n \n \t\t/* Configure the format. */\n-\t\tret = video_->getFormat(&format_);\n+\t\tV4L2DeviceFormat format;\n+\t\tret = video_->getFormat(&format);\n \t\tif (ret) {\n-\t\t\tstd::cout << \"Failed to get format on output device\" << std::endl;\n+\t\t\tcout << \"Failed to get format on output device\" << std::endl;\n \t\t\treturn ret;\n \t\t}\n \n-\t\tformat_.size.width = 1920;\n-\t\tformat_.size.height = 1080;\n-\t\tformat_.fourcc = V4L2_PIX_FMT_RGB24;\n-\t\tformat_.planesCount = 1;\n-\t\tformat_.planes[0].size = 1920 * 1080 * 3;\n-\t\tformat_.planes[0].bpl = 1920 * 3;\n-\n-\t\tif (video_->setFormat(&format_)) {\n-\t\t\tcleanup();\n+\t\tformat.size = config.size;\n+\t\tformat.fourcc = V4L2VideoDevice::toV4L2Fourcc(config.pixelFormat, false);\n+\t\tif (video_->setFormat(&format))\n \t\t\treturn TestFail;\n-\t\t}\n \n-\t\t/* Export the buffers to a pool. */\n-\t\tpool_.createBuffers(SINK_BUFFER_COUNT);\n-\t\tret = video_->exportBuffers(&pool_);\n-\t\tif (ret) {\n-\t\t\tstd::cout << \"Failed to export buffers\" << std::endl;\n-\t\t\tcleanup();\n-\t\t\treturn TestFail;\n-\t\t}\n-\n-\t\t/* Only use the first CAMERA_BUFFER_COUNT buffers to start with. */\n-\t\tavailableBuffers_.resize(CAMERA_BUFFER_COUNT);\n-\t\tstd::iota(availableBuffers_.begin(), availableBuffers_.end(), 0);\n-\n-\t\t/* Connect the buffer ready signal. */\n-\t\tvideo_->bufferReady.connect(this, &FrameSink::bufferComplete);\n-\n-\t\treturn TestPass;\n+\t\treturn video_->allocateBuffers(config.bufferCount, &buffers_);\n \t}\n \n-\tvoid cleanup()\n-\t{\n-\t\tif (video_) {\n-\t\t\tvideo_->streamOff();\n-\t\t\tvideo_->releaseBuffers();\n-\t\t\tvideo_->close();\n-\n-\t\t\tdelete video_;\n-\t\t\tvideo_ = nullptr;\n-\t\t}\n-\n-\t\tif (media_)\n-\t\t\tmedia_->release();\n-\t}\n-\n-\tint start()\n-\t{\n-\t\trequestsCount_ = 0;\n-\t\tdone_ = false;\n-\n-\t\tint ret = video_->streamOn();\n-\t\tif (ret < 0)\n-\t\t\treturn ret;\n-\n-\t\t/* Queue all the initial requests. */\n-\t\tfor (unsigned int index = 0; index < CAMERA_BUFFER_COUNT; ++index)\n-\t\t\tqueueRequest(index);\n-\n-\t\treturn 0;\n-\t}\n-\n-\tint stop()\n-\t{\n-\t\treturn video_->streamOff();\n-\t}\n-\n-\tvoid requestComplete(uint64_t cookie, const Buffer *metadata)\n-\t{\n-\t\tunsigned int index = cookie;\n-\n-\t\tBuffer *buffer = new Buffer(index, metadata);\n-\t\tint ret = video_->queueBuffer(buffer);\n-\t\tif (ret < 0)\n-\t\t\tstd::cout << \"Failed to queue buffer to sink\" << std::endl;\n-\t}\n-\n-\tbool done() const { return done_; }\n-\n-\tPixelFormat format() const\n-\t{\n-\t\treturn video_->toPixelFormat(format_.fourcc);\n-\t}\n-\n-\tconst Size &size() const\n-\t{\n-\t\treturn format_.size;\n-\t}\n-\n-\tSignal<uint64_t, int> requestReady;\n+\tconst vector<FrameBuffer *> &buffers() { return buffers_; };\n \n private:\n-\tvoid queueRequest(unsigned int index)\n-\t{\n-\t\tauto it = std::find(availableBuffers_.begin(),\n-\t\t\t\t availableBuffers_.end(), index);\n-\t\tavailableBuffers_.erase(it);\n-\n-\t\tuint64_t cookie = index;\n-\t\tBufferMemory &mem = pool_.buffers()[index];\n-\t\tint dmabuf = mem.planes()[0].fd();\n-\n-\t\trequestReady.emit(cookie, dmabuf);\n-\n-\t\trequestsCount_++;\n-\t}\n-\n-\tvoid bufferComplete(Buffer *buffer)\n-\t{\n-\t\tavailableBuffers_.push_back(buffer->index());\n-\n-\t\t/*\n-\t\t * Pick the buffer for the next request among the available\n-\t\t * buffers.\n-\t\t *\n-\t\t * For the first 20 frames, select the buffer that has just\n-\t\t * completed to keep the mapping of dmabuf fds to buffers\n-\t\t * unchanged in the camera.\n-\t\t *\n-\t\t * For the next 20 frames, cycle randomly over the available\n-\t\t * buffers. The mapping should still be kept unchanged, as the\n-\t\t * camera should map using the cached fds.\n-\t\t *\n-\t\t * For the last 20 frames, cycles through all buffers, which\n-\t\t * should trash the mappings.\n-\t\t */\n-\t\tunsigned int index = buffer->index();\n-\t\tdelete buffer;\n-\n-\t\tstd::cout << \"Completed buffer, request=\" << requestsCount_\n-\t\t\t << \", available buffers=\" << availableBuffers_.size()\n-\t\t\t << std::endl;\n-\n-\t\tif (requestsCount_ >= 60) {\n-\t\t\tif (availableBuffers_.size() == SINK_BUFFER_COUNT)\n-\t\t\t\tdone_ = true;\n-\t\t\treturn;\n-\t\t}\n-\n-\t\tif (requestsCount_ == 40) {\n-\t\t\t/* Add the remaining of the buffers. */\n-\t\t\tfor (unsigned int i = CAMERA_BUFFER_COUNT;\n-\t\t\t i < SINK_BUFFER_COUNT; ++i)\n-\t\t\t\tavailableBuffers_.push_back(i);\n-\t\t}\n-\n-\t\tif (requestsCount_ >= 20) {\n-\t\t\t/*\n-\t\t\t * Wait until we have enough buffers to make this\n-\t\t\t * meaningful. Preferably half of the camera buffers,\n-\t\t\t * but no less than 2 in any case.\n-\t\t\t */\n-\t\t\tconst unsigned int min_pool_size =\n-\t\t\t\tstd::min(CAMERA_BUFFER_COUNT / 2, 2U);\n-\t\t\tif (availableBuffers_.size() < min_pool_size)\n-\t\t\t\treturn;\n-\n-\t\t\t/* Pick a buffer at random. */\n-\t\t\tunsigned int pos = random_() % availableBuffers_.size();\n-\t\t\tindex = availableBuffers_[pos];\n-\t\t}\n-\n-\t\tqueueRequest(index);\n-\t}\n-\n-\tstd::shared_ptr<MediaDevice> media_;\n+\tshared_ptr<MediaDevice> media_;\n \tV4L2VideoDevice *video_;\n-\tBufferPool pool_;\n-\tV4L2DeviceFormat format_;\n-\n-\tunsigned int requestsCount_;\n-\tstd::vector<int> availableBuffers_;\n-\tstd::random_device random_;\n-\n-\tbool done_;\n+\tvector<FrameBuffer *> buffers_;\n };\n \n-class BufferImportTest : public CameraTest, public Test\n+class BufferImport : public CameraTest, public Test\n {\n public:\n-\tBufferImportTest()\n+\tBufferImport()\n \t\t: CameraTest(\"VIMC Sensor B\")\n \t{\n \t}\n \n-\tvoid queueRequest(uint64_t cookie, int dmabuf)\n+protected:\n+\tunsigned int completeBuffersCount_;\n+\tunsigned int completeRequestsCount_;\n+\n+\tvoid bufferComplete(Request *request, FrameBuffer *buffer)\n \t{\n-\t\tRequest *request = camera_->createRequest(cookie);\n+\t\tif (buffer->info().status() != BufferInfo::BufferSuccess)\n+\t\t\treturn;\n \n-\t\tstd::unique_ptr<Buffer> buffer = stream_->createBuffer({ dmabuf, -1, -1 });\n-\t\trequest->addBuffer(stream_, move(buffer));\n-\t\tcamera_->queueRequest(request);\n+\t\tcompleteBuffersCount_++;\n \t}\n \n-protected:\n-\tvoid bufferComplete(Request *request, Buffer *buffer)\n+\tvoid requestComplete(Request *request)\n \t{\n-\t\tif (buffer->info().status() != BufferInfo::BufferSuccess)\n+\t\tif (request->status() != Request::RequestComplete)\n \t\t\treturn;\n \n-\t\tunsigned int index = buffer->index();\n-\t\tint dmabuf = buffer->dmabufs()[0];\n+\t\tconst map<Stream *, FrameBuffer *> &buffers = request->buffers();\n \n-\t\t/* Record dmabuf to index remappings. */\n-\t\tbool remapped = false;\n-\t\tif (bufferMappings_.find(index) != bufferMappings_.end()) {\n-\t\t\tif (bufferMappings_[index] != dmabuf)\n-\t\t\t\tremapped = true;\n-\t\t}\n+\t\tcompleteRequestsCount_++;\n \n-\t\tstd::cout << \"Completed request \" << framesCaptured_\n-\t\t\t << \": dmabuf fd \" << dmabuf\n-\t\t\t << \" -> index \" << index\n-\t\t\t << \" (\" << (remapped ? 'R' : '-') << \")\"\n-\t\t\t << std::endl;\n+\t\t/* Create a new request. */\n+\t\tStream *stream = buffers.begin()->first;\n+\t\tFrameBuffer *buffer = buffers.begin()->second;\n \n-\t\tif (remapped)\n-\t\t\tbufferRemappings_.push_back(framesCaptured_);\n+\t\trequest = camera_->createRequest();\n+\t\trequest->addBuffer(stream, buffer);\n+\t\tcamera_->queueRequest(request);\n+\t}\n \n-\t\tbufferMappings_[index] = dmabuf;\n-\t\tframesCaptured_++;\n+\tint init() override\n+\t{\n+\t\tif (status_ != TestPass)\n+\t\t\treturn status_;\n \n-\t\tsink_.requestComplete(request->cookie(), buffer);\n+\t\tconfig_ = camera_->generateConfiguration({ StreamRole::VideoRecording });\n+\t\tif (!config_ || config_->size() != 1) {\n+\t\t\tcout << \"Failed to generate default configuration\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n \n-\t\tif (framesCaptured_ == 60)\n-\t\t\tsink_.stop();\n+\t\treturn TestPass;\n \t}\n \n-\tint initCamera()\n+\tint run() override\n \t{\n-\t\tif (camera_->acquire()) {\n-\t\t\tstd::cout << \"Failed to acquire the camera\" << std::endl;\n-\t\t\treturn TestFail;\n-\t\t}\n+\t\tStreamConfiguration &cfg = config_->at(0);\n \n-\t\t/*\n-\t\t * Configure the Stream to work with externally allocated\n-\t\t * buffers by setting the memoryType to ExternalMemory.\n-\t\t */\n-\t\tstd::unique_ptr<CameraConfiguration> config;\n-\t\tconfig = camera_->generateConfiguration({ StreamRole::VideoRecording });\n-\t\tif (!config || config->size() != 1) {\n-\t\t\tstd::cout << \"Failed to generate configuration\" << std::endl;\n+\t\tif (camera_->acquire()) {\n+\t\t\tcout << \"Failed to acquire the camera\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tStreamConfiguration &cfg = config->at(0);\n-\t\tcfg.size = sink_.size();\n-\t\tcfg.pixelFormat = sink_.format();\n-\t\tcfg.bufferCount = CAMERA_BUFFER_COUNT;\n-\t\tcfg.memoryType = ExternalMemory;\n-\n-\t\tif (camera_->configure(config.get())) {\n-\t\t\tstd::cout << \"Failed to set configuration\" << std::endl;\n+\t\tif (camera_->configure(config_.get())) {\n+\t\t\tcout << \"Failed to set default configuration\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tstream_ = cfg.stream();\n-\n-\t\t/* Allocate buffers. */\n \t\tif (camera_->allocateBuffers()) {\n-\t\t\tstd::cout << \"Failed to allocate buffers\" << std::endl;\n+\t\t\tcout << \"Failed to allocate buffers\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\t/* Connect the buffer completed signal. */\n-\t\tcamera_->bufferCompleted.connect(this, &BufferImportTest::bufferComplete);\n+\t\tStream *stream = cfg.stream();\n \n-\t\treturn TestPass;\n-\t}\n+\t\tBufferSource source;\n+\t\tint ret = source.allocate(cfg);\n+\t\tif (ret < 0)\n+\t\t\treturn TestFail;\n \n-\tint init()\n-\t{\n-\t\tif (status_ != TestPass)\n-\t\t\treturn status_;\n+\t\tvector<Request *> requests;\n+\t\tfor (FrameBuffer *buffer : source.buffers()) {\n+\t\t\tRequest *request = camera_->createRequest();\n+\t\t\tif (!request) {\n+\t\t\t\tcout << \"Failed to create request\" << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n \n-\t\tint ret = sink_.init();\n-\t\tif (ret != TestPass) {\n-\t\t\tcleanup();\n-\t\t\treturn ret;\n-\t\t}\n+\t\t\tif (request->addBuffer(stream, buffer)) {\n+\t\t\t\tcout << \"Failed to associating buffer with request\" << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n \n-\t\tret = initCamera();\n-\t\tif (ret != TestPass) {\n-\t\t\tcleanup();\n-\t\t\treturn ret;\n+\t\t\trequests.push_back(request);\n \t\t}\n \n-\t\tsink_.requestReady.connect(this, &BufferImportTest::queueRequest);\n-\t\treturn TestPass;\n-\t}\n-\n-\tint run()\n-\t{\n-\t\tint ret;\n+\t\tcompleteRequestsCount_ = 0;\n+\t\tcompleteBuffersCount_ = 0;\n \n-\t\tframesCaptured_ = 0;\n+\t\tcamera_->bufferCompleted.connect(this, &BufferImport::bufferComplete);\n+\t\tcamera_->requestCompleted.connect(this, &BufferImport::requestComplete);\n \n \t\tif (camera_->start()) {\n-\t\t\tstd::cout << \"Failed to start camera\" << std::endl;\n+\t\t\tcout << \"Failed to start camera\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tret = sink_.start();\n-\t\tif (ret < 0) {\n-\t\t\tstd::cout << \"Failed to start sink\" << std::endl;\n-\t\t\treturn TestFail;\n+\t\tfor (Request *request : requests) {\n+\t\t\tif (camera_->queueRequest(request)) {\n+\t\t\t\tcout << \"Failed to queue request\" << endl;\n+\t\t\t\treturn TestFail;\n+\t\t\t}\n \t\t}\n \n \t\tEventDispatcher *dispatcher = cm_->eventDispatcher();\n \n \t\tTimer timer;\n-\t\ttimer.start(5000);\n-\t\twhile (timer.isRunning() && !sink_.done())\n+\t\ttimer.start(1000);\n+\t\twhile (timer.isRunning())\n \t\t\tdispatcher->processEvents();\n \n-\t\tstd::cout << framesCaptured_ << \" frames captured, \"\n-\t\t\t << bufferRemappings_.size() << \" buffers remapped\"\n-\t\t\t << std::endl;\n+\t\tunsigned int nbuffers = source.buffers().size();\n \n-\t\tif (framesCaptured_ < 60) {\n-\t\t\tstd::cout << \"Too few frames captured\" << std::endl;\n+\t\tif (completeRequestsCount_ <= nbuffers * 2) {\n+\t\t\tcout << \"Failed to capture enough frames (got \"\n+\t\t\t << completeRequestsCount_ << \" expected at least \"\n+\t\t\t << nbuffers * 2 << \")\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tif (bufferRemappings_.empty()) {\n-\t\t\tstd::cout << \"No buffer remappings\" << std::endl;\n+\t\tif (completeRequestsCount_ != completeBuffersCount_) {\n+\t\t\tcout << \"Number of completed buffers and requests differ\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tif (bufferRemappings_[0] < 40) {\n-\t\t\tstd::cout << \"Early buffer remapping\" << std::endl;\n+\t\tif (camera_->stop()) {\n+\t\t\tcout << \"Failed to stop camera\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\treturn TestPass;\n-\t}\n-\n-\tvoid cleanup()\n-\t{\n-\t\tsink_.cleanup();\n+\t\tif (camera_->freeBuffers()) {\n+\t\t\tcout << \"Failed to free buffers\" << endl;\n+\t\t\treturn TestFail;\n+\t\t}\n \n-\t\tcamera_->stop();\n-\t\tcamera_->freeBuffers();\n+\t\treturn TestPass;\n \t}\n \n-private:\n-\tStream *stream_;\n-\n-\tstd::map<unsigned int, int> bufferMappings_;\n-\tstd::vector<unsigned int> bufferRemappings_;\n-\tunsigned int framesCaptured_;\n-\n-\tFrameSink sink_;\n+\tunique_ptr<CameraConfiguration> config_;\n };\n \n-TEST_REGISTER(BufferImportTest);\n+} /* namespace */\n+\n+TEST_REGISTER(BufferImport);\ndiff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\nindex 8307ea2629801679..ee6d0a1bc1fa839d 100644\n--- a/test/camera/capture.cpp\n+++ b/test/camera/capture.cpp\n@@ -26,7 +26,7 @@ protected:\n \tunsigned int completeBuffersCount_;\n \tunsigned int completeRequestsCount_;\n \n-\tvoid bufferComplete(Request *request, Buffer *buffer)\n+\tvoid bufferComplete(Request *request, FrameBuffer *buffer)\n \t{\n \t\tif (buffer->info().status() != BufferInfo::BufferSuccess)\n \t\t\treturn;\n@@ -39,17 +39,16 @@ protected:\n \t\tif (request->status() != Request::RequestComplete)\n \t\t\treturn;\n \n-\t\tconst std::map<Stream *, Buffer *> &buffers = request->buffers();\n+\t\tconst std::map<Stream *, FrameBuffer *> &buffers = request->buffers();\n \n \t\tcompleteRequestsCount_++;\n \n \t\t/* Create a new request. */\n \t\tStream *stream = buffers.begin()->first;\n-\t\tBuffer *buffer = buffers.begin()->second;\n-\t\tstd::unique_ptr<Buffer> newBuffer = stream->createBuffer(buffer->index());\n+\t\tFrameBuffer *buffer = buffers.begin()->second;\n \n \t\trequest = camera_->createRequest();\n-\t\trequest->addBuffer(stream, std::move(newBuffer));\n+\t\trequest->addBuffer(stream, buffer);\n \t\tcamera_->queueRequest(request);\n \t}\n \n@@ -87,21 +86,21 @@ protected:\n \t\t}\n \n \t\tStream *stream = cfg.stream();\n+\n+\t\tBufferAllocator allocator(camera_);\n+\t\tint ret = allocator.allocate(stream, cfg);\n+\t\tif (ret < 0)\n+\t\t\treturn TestFail;\n+\n \t\tstd::vector<Request *> requests;\n-\t\tfor (unsigned int i = 0; i < cfg.bufferCount; ++i) {\n+\t\tfor (FrameBuffer *buffer : allocator.buffers(stream)) {\n \t\t\tRequest *request = camera_->createRequest();\n \t\t\tif (!request) {\n \t\t\t\tcout << \"Failed to create request\" << endl;\n \t\t\t\treturn TestFail;\n \t\t\t}\n \n-\t\t\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(i);\n-\t\t\tif (!buffer) {\n-\t\t\t\tcout << \"Failed to create buffer \" << i << endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\n-\t\t\tif (request->addBuffer(stream, std::move(buffer))) {\n+\t\t\tif (request->addBuffer(stream, buffer)) {\n \t\t\t\tcout << \"Failed to associating buffer with request\" << endl;\n \t\t\t\treturn TestFail;\n \t\t\t}\n@@ -134,10 +133,12 @@ protected:\n \t\twhile (timer.isRunning())\n \t\t\tdispatcher->processEvents();\n \n-\t\tif (completeRequestsCount_ <= cfg.bufferCount * 2) {\n+\t\tunsigned int nbuffers = allocator.buffers(stream).size();\n+\n+\t\tif (completeRequestsCount_ <= nbuffers * 2) {\n \t\t\tcout << \"Failed to capture enough frames (got \"\n \t\t\t << completeRequestsCount_ << \" expected at least \"\n-\t\t\t << cfg.bufferCount * 2 << \")\" << endl;\n+\t\t\t << nbuffers * 2 << \")\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \ndiff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp\nindex f627b8f37422350e..e9468d806f977a63 100644\n--- a/test/camera/statemachine.cpp\n+++ b/test/camera/statemachine.cpp\n@@ -185,6 +185,12 @@ protected:\n \t\tif (camera_->allocateBuffers())\n \t\t\treturn TestFail;\n \n+\t\t/* Use internally allocated buffers. */\n+\t\tallocator_ = new BufferAllocator(camera_);\n+\t\tStream *stream = *camera_->streams().begin();\n+\t\tif (allocator_->allocate(stream, defconf_->at(0)) < 0)\n+\t\t\treturn TestFail;\n+\n \t\tif (camera_->start())\n \t\t\treturn TestFail;\n \n@@ -218,8 +224,7 @@ protected:\n \t\t\treturn TestFail;\n \n \t\tStream *stream = *camera_->streams().begin();\n-\t\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(0);\n-\t\tif (request->addBuffer(stream, std::move(buffer)))\n+\t\tif (request->addBuffer(stream, allocator_->buffers(stream)[0]))\n \t\t\treturn TestFail;\n \n \t\tif (camera_->queueRequest(request))\n@@ -229,6 +234,8 @@ protected:\n \t\tif (camera_->stop())\n \t\t\treturn TestFail;\n \n+\t\tdelete allocator_;\n+\n \t\tif (camera_->freeBuffers())\n \t\t\treturn TestFail;\n \n@@ -283,6 +290,7 @@ protected:\n \t}\n \n \tstd::unique_ptr<CameraConfiguration> defconf_;\n+\tBufferAllocator *allocator_;\n };\n \n } /* namespace */\ndiff --git a/test/v4l2_videodevice/buffer_sharing.cpp b/test/v4l2_videodevice/buffer_sharing.cpp\nindex 6b71caef111693d6..1b6ff756ac53a96a 100644\n--- a/test/v4l2_videodevice/buffer_sharing.cpp\n+++ b/test/v4l2_videodevice/buffer_sharing.cpp\n@@ -73,16 +73,14 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tpool_.createBuffers(bufferCount);\n-\n-\t\tret = capture_->exportBuffers(&pool_);\n-\t\tif (ret) {\n-\t\t\tstd::cout << \"Failed to export buffers\" << std::endl;\n+\t\tret = capture_->allocateBuffers(bufferCount, &buffers_);\n+\t\tif (ret < 0) {\n+\t\t\tstd::cout << \"Failed to allocate buffers\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tret = output_->importBuffers(&pool_);\n-\t\tif (ret) {\n+\t\tret = output_->externalBuffers(bufferCount);\n+\t\tif (ret < 0) {\n \t\t\tstd::cout << \"Failed to import buffers\" << std::endl;\n \t\t\treturn TestFail;\n \t\t}\n@@ -90,7 +88,7 @@ protected:\n \t\treturn 0;\n \t}\n \n-\tvoid captureBufferReady(Buffer *buffer)\n+\tvoid captureBufferReady(FrameBuffer *buffer)\n \t{\n \t\tconst BufferInfo &info = buffer->info();\n \n@@ -101,10 +99,11 @@ protected:\n \t\t\treturn;\n \n \t\toutput_->queueBuffer(buffer);\n+\n \t\tframesCaptured_++;\n \t}\n \n-\tvoid outputBufferReady(Buffer *buffer)\n+\tvoid outputBufferReady(FrameBuffer *buffer)\n \t{\n \t\tconst BufferInfo &info = buffer->info();\n \n@@ -127,10 +126,8 @@ protected:\n \t\tcapture_->bufferReady.connect(this, &BufferSharingTest::captureBufferReady);\n \t\toutput_->bufferReady.connect(this, &BufferSharingTest::outputBufferReady);\n \n-\t\tstd::vector<std::unique_ptr<Buffer>> buffers;\n-\t\tbuffers = capture_->queueAllBuffers();\n-\t\tif (buffers.empty())\n-\t\t\treturn TestFail;\n+\t\tfor (FrameBuffer *buffer : buffers_)\n+\t\t\tcapture_->queueBuffer(buffer);\n \n \t\tret = capture_->streamOn();\n \t\tif (ret) {\ndiff --git a/test/v4l2_videodevice/capture_async.cpp b/test/v4l2_videodevice/capture_async.cpp\nindex 0bc0067c50909c9d..78ac34c4799e9e5a 100644\n--- a/test/v4l2_videodevice/capture_async.cpp\n+++ b/test/v4l2_videodevice/capture_async.cpp\n@@ -20,7 +20,7 @@ public:\n \tCaptureAsyncTest()\n \t\t: V4L2VideoDeviceTest(\"vimc\", \"Raw Capture 0\"), frames(0) {}\n \n-\tvoid receiveBuffer(Buffer *buffer)\n+\tvoid receiveBuffer(FrameBuffer *buffer)\n \t{\n \t\tstd::cout << \"Received buffer\" << std::endl;\n \t\tframes++;\n@@ -38,18 +38,14 @@ protected:\n \t\tTimer timeout;\n \t\tint ret;\n \n-\t\tpool_.createBuffers(bufferCount);\n-\n-\t\tret = capture_->exportBuffers(&pool_);\n-\t\tif (ret)\n+\t\tret = capture_->allocateBuffers(bufferCount, &buffers_);\n+\t\tif (ret < 0)\n \t\t\treturn TestFail;\n \n \t\tcapture_->bufferReady.connect(this, &CaptureAsyncTest::receiveBuffer);\n \n-\t\tstd::vector<std::unique_ptr<Buffer>> buffers;\n-\t\tbuffers = capture_->queueAllBuffers();\n-\t\tif (buffers.empty())\n-\t\t\treturn TestFail;\n+\t\tfor (FrameBuffer *buffer : buffers_)\n+\t\t\tcapture_->queueBuffer(buffer);\n \n \t\tret = capture_->streamOn();\n \t\tif (ret)\ndiff --git a/test/v4l2_videodevice/request_buffers.cpp b/test/v4l2_videodevice/request_buffers.cpp\nindex c4aedf7b3cd61e80..2f8dfe1cafb111df 100644\n--- a/test/v4l2_videodevice/request_buffers.cpp\n+++ b/test/v4l2_videodevice/request_buffers.cpp\n@@ -16,17 +16,10 @@ public:\n protected:\n \tint run()\n \t{\n-\t\t/*\n-\t\t * TODO:\n-\t\t * Test invalid requests\n-\t\t * Test different buffer allocations\n-\t\t */\n \t\tconst unsigned int bufferCount = 8;\n \n-\t\tpool_.createBuffers(bufferCount);\n-\n-\t\tint ret = capture_->exportBuffers(&pool_);\n-\t\tif (ret)\n+\t\tint ret = capture_->allocateBuffers(bufferCount, &buffers_);\n+\t\tif (ret != bufferCount)\n \t\t\treturn TestFail;\n \n \t\treturn TestPass;\ndiff --git a/test/v4l2_videodevice/stream_on_off.cpp b/test/v4l2_videodevice/stream_on_off.cpp\nindex 7664adc4c1f07046..ce48310aa2b7c3a8 100644\n--- a/test/v4l2_videodevice/stream_on_off.cpp\n+++ b/test/v4l2_videodevice/stream_on_off.cpp\n@@ -17,10 +17,8 @@ protected:\n \t{\n \t\tconst unsigned int bufferCount = 8;\n \n-\t\tpool_.createBuffers(bufferCount);\n-\n-\t\tint ret = capture_->exportBuffers(&pool_);\n-\t\tif (ret)\n+\t\tint ret = capture_->allocateBuffers(bufferCount, &buffers_);\n+\t\tif (ret < 0)\n \t\t\treturn TestFail;\n \n \t\tret = capture_->streamOn();\ndiff --git a/test/v4l2_videodevice/v4l2_m2mdevice.cpp b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\nindex 442bcac5dc49cc59..2dd4b9440b4d4d71 100644\n--- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n+++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n@@ -29,7 +29,7 @@ public:\n \t{\n \t}\n \n-\tvoid outputBufferComplete(Buffer *buffer)\n+\tvoid outputBufferComplete(FrameBuffer *buffer)\n \t{\n \t\tcout << \"Received output buffer\" << endl;\n \n@@ -39,7 +39,7 @@ public:\n \t\tvim2m_->output()->queueBuffer(buffer);\n \t}\n \n-\tvoid receiveCaptureBuffer(Buffer *buffer)\n+\tvoid receiveCaptureBuffer(FrameBuffer *buffer)\n \t{\n \t\tcout << \"Received capture buffer\" << endl;\n \n@@ -112,17 +112,14 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tcapturePool_.createBuffers(bufferCount);\n-\t\toutputPool_.createBuffers(bufferCount);\n-\n-\t\tret = capture->exportBuffers(&capturePool_);\n-\t\tif (ret) {\n+\t\tret = capture->allocateBuffers(bufferCount, &captureBuffers_);\n+\t\tif (ret < 0) {\n \t\t\tcerr << \"Failed to export Capture Buffers\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tret = output->exportBuffers(&outputPool_);\n-\t\tif (ret) {\n+\t\tret = output->allocateBuffers(bufferCount, &outputBuffers_);\n+\t\tif (ret < 0) {\n \t\t\tcerr << \"Failed to export Output Buffers\" << endl;\n \t\t\treturn TestFail;\n \t\t}\n@@ -130,24 +127,11 @@ protected:\n \t\tcapture->bufferReady.connect(this, &V4L2M2MDeviceTest::receiveCaptureBuffer);\n \t\toutput->bufferReady.connect(this, &V4L2M2MDeviceTest::outputBufferComplete);\n \n-\t\tstd::vector<std::unique_ptr<Buffer>> captureBuffers;\n-\t\tcaptureBuffers = capture->queueAllBuffers();\n-\t\tif (captureBuffers.empty()) {\n-\t\t\tcerr << \"Failed to queue all Capture Buffers\" << endl;\n-\t\t\treturn TestFail;\n-\t\t}\n+\t\tfor (FrameBuffer *buffer : captureBuffers_)\n+\t\t\tcapture->queueBuffer(buffer);\n \n-\t\t/* We can't \"queueAllBuffers()\" on an output device, so we do it manually */\n-\t\tstd::vector<std::unique_ptr<Buffer>> outputBuffers;\n-\t\tfor (unsigned int i = 0; i < outputPool_.count(); ++i) {\n-\t\t\tBuffer *buffer = new Buffer(i);\n-\t\t\toutputBuffers.emplace_back(buffer);\n-\t\t\tret = output->queueBuffer(buffer);\n-\t\t\tif (ret) {\n-\t\t\t\tcerr << \"Failed to queue output buffer\" << i << endl;\n-\t\t\t\treturn TestFail;\n-\t\t\t}\n-\t\t}\n+\t\tfor (FrameBuffer *buffer : outputBuffers_)\n+\t\t\toutput->queueBuffer(buffer);\n \n \t\tret = capture->streamOn();\n \t\tif (ret) {\n@@ -202,8 +186,8 @@ private:\n \tstd::shared_ptr<MediaDevice> media_;\n \tV4L2M2MDevice *vim2m_;\n \n-\tBufferPool capturePool_;\n-\tBufferPool outputPool_;\n+\tstd::vector<FrameBuffer *> captureBuffers_;\n+\tstd::vector<FrameBuffer *> outputBuffers_;\n \n \tunsigned int outputFrames_;\n \tunsigned int captureFrames_;\ndiff --git a/test/v4l2_videodevice/v4l2_videodevice_test.h b/test/v4l2_videodevice/v4l2_videodevice_test.h\nindex 34dd231c6d9d108d..02800c2a16f7b0f7 100644\n--- a/test/v4l2_videodevice/v4l2_videodevice_test.h\n+++ b/test/v4l2_videodevice/v4l2_videodevice_test.h\n@@ -41,7 +41,7 @@ protected:\n \tCameraSensor *sensor_;\n \tV4L2Subdevice *debayer_;\n \tV4L2VideoDevice *capture_;\n-\tBufferPool pool_;\n+\tstd::vector<FrameBuffer *> buffers_;\n };\n \n #endif /* __LIBCAMERA_V4L2_DEVICE_TEST_H_ */\n", "prefixes": [ "libcamera-devel", "27/30" ] }