Show a patch.

GET /api/patches/2608/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2608,
    "url": "https://patchwork.libcamera.org/api/patches/2608/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/2608/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20200112010212.2609025-27-niklas.soderlund@ragnatech.se>",
    "date": "2020-01-12T01:02:06",
    "name": "[libcamera-devel,v4,26/32] libcamera: Switch to FrameBuffer interface",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "19cb8f4e64541b3b5d7cb1b576cfb4c43fd63b15",
    "submitter": {
        "id": 5,
        "url": "https://patchwork.libcamera.org/api/people/5/?format=api",
        "name": "Niklas Söderlund",
        "email": "niklas.soderlund@ragnatech.se"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/2608/mbox/",
    "series": [
        {
            "id": 617,
            "url": "https://patchwork.libcamera.org/api/series/617/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=617",
            "date": "2020-01-12T01:01:40",
            "name": "libcamera: Rework buffer API",
            "version": 4,
            "mbox": "https://patchwork.libcamera.org/series/617/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/2608/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/2608/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 D3909606E2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun, 12 Jan 2020 02:03:24 +0100 (CET)",
            "from bismarck.berto.se (p54ac5d7b.dip0.t-ipconnect.de\n\t[84.172.93.123]) by bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA\n\tid 5216d22a-34d7-11ea-b6d8-005056917f90;\n\tSun, 12 Jan 2020 02:03:19 +0100 (CET)"
        ],
        "X-Halon-ID": "5216d22a-34d7-11ea-b6d8-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": "Sun, 12 Jan 2020 02:02:06 +0100",
        "Message-Id": "<20200112010212.2609025-27-niklas.soderlund@ragnatech.se>",
        "X-Mailer": "git-send-email 2.24.1",
        "In-Reply-To": "<20200112010212.2609025-1-niklas.soderlund@ragnatech.se>",
        "References": "<20200112010212.2609025-1-niklas.soderlund@ragnatech.se>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH v4 26/32] 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": "Sun, 12 Jan 2020 01:03:25 -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\nFrameBufferAllocator helper.\n\nFollow-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>\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n* Changes since v3\n- Pass buffer index in v4l2 component using the request cookie instead\n  of the buffer cookie. Thanks Laurent for a great suggestion.\n\n* Changes since v2\n- Fix compile tested only code path in the HAL\n- Update documentation\n- Add a setCookie() call in the V4L2 compat layer which was lost in\n  Laurent's protoype patch which lead to the index handling in V4L2 to\n  break.\n---\n include/libcamera/camera.h               |   4 +-\n include/libcamera/request.h              |  14 +--\n src/android/camera_device.cpp            |  31 ++++--\n src/cam/buffer_writer.cpp                |   5 +-\n src/cam/buffer_writer.h                  |   3 +-\n src/cam/capture.cpp                      |  42 ++++----\n src/cam/capture.h                        |   5 +-\n src/libcamera/camera.cpp                 |  48 +++------\n src/libcamera/include/pipeline_handler.h |   5 +-\n src/libcamera/pipeline/ipu3/ipu3.cpp     | 124 +++++++----------------\n src/libcamera/pipeline/rkisp1/rkisp1.cpp |  41 ++------\n src/libcamera/pipeline/uvcvideo.cpp      |  22 ++--\n src/libcamera/pipeline/vimc.cpp          |  22 ++--\n src/libcamera/pipeline_handler.cpp       |   2 +-\n src/libcamera/request.cpp                |  30 +++---\n src/qcam/main_window.cpp                 |  44 ++++----\n src/qcam/main_window.h                   |   6 +-\n src/v4l2/v4l2_camera.cpp                 |  60 ++++++-----\n src/v4l2/v4l2_camera.h                   |  39 +++----\n src/v4l2/v4l2_camera_proxy.cpp           |  17 ++--\n test/camera/buffer_import.cpp            |  19 ++--\n test/camera/capture.cpp                  |  38 ++++---\n test/camera/statemachine.cpp             |  12 ++-\n 23 files changed, 266 insertions(+), 367 deletions(-)",
    "diff": "diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h\nindex 2fd5870b15c69087..28655bec9ebc89ce 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 FrameBufferAllocator;\n class PipelineHandler;\n class Request;\n@@ -78,7 +78,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 b832422482645f25..eded68318b7dbb3c 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@@ -52,14 +52,14 @@ private:\n \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 ebe91ea8af4f6436..ab3e44889f8b7ada 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -739,13 +739,21 @@ 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+\tfor (int i = 0; i < 3; i++) {\n+\t\tFrameBuffer::Plane plane;\n+\t\tplane.fd = FileDescriptor(camera3Handle->data[i]);\n+\t\t/*\n+\t\t * Setting length to zero here is OK as the length is only used\n+\t\t * to map the memory of the plane. Libcamera do not need to poke\n+\t\t * at the memory content queued by the HAL.\n+\t\t */\n+\t\tplane.length = 0;\n+\t\tplanes.push_back(std::move(plane));\n+\t}\n+\n+\tFrameBuffer *buffer = new FrameBuffer(std::move(planes));\n \tif (!buffer) {\n \t\tLOG(HAL, Error) << \"Failed to create buffer\";\n \t\tdelete descriptor;\n@@ -754,7 +762,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 +779,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 *buffer = buffers.begin()->second;\n \tcamera3_buffer_status status = CAMERA3_BUFFER_STATUS_OK;\n \tstd::unique_ptr<CameraMetadata> resultMetadata;\n \n@@ -803,11 +811,11 @@ void CameraDevice::requestComplete(Request *request)\n \n \tif (status == CAMERA3_BUFFER_STATUS_OK) {\n \t\tnotifyShutter(descriptor->frameNumber,\n-\t\t\t      libcameraBuffer->metadata().timestamp);\n+\t\t\t      buffer->metadata().timestamp);\n \n \t\tcaptureResult.partial_result = 1;\n \t\tresultMetadata = getResultMetadata(descriptor->frameNumber,\n-\t\t\t\t\t\t   libcameraBuffer->metadata().timestamp);\n+\t\t\t\t\t\t   buffer->metadata().timestamp);\n \t\tcaptureResult.result = resultMetadata->get();\n \t}\n \n@@ -825,6 +833,7 @@ void CameraDevice::requestComplete(Request *request)\n \tcallbacks_->process_capture_result(callbacks_, &captureResult);\n \n \tdelete descriptor;\n+\tdelete buffer;\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 41ef4b0a36d61b03..1d7366c87714cd91 100644\n--- a/src/cam/buffer_writer.cpp\n+++ b/src/cam/buffer_writer.cpp\n@@ -22,7 +22,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@@ -43,8 +43,7 @@ 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 (const FrameBuffer::Plane &plane : mem->planes()) {\n+\tfor (const FrameBuffer::Plane &plane : buffer->planes()) {\n \t\t/* \\todo Once the FrameBuffer is done cache mapped memory. */\n \t\tvoid *data = mmap(NULL, plane.length, PROT_READ, MAP_SHARED,\n \t\t\t\t  plane.fd.fd(), 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 da942f56118983bd..dd078eb0ae4a2c62 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+\tFrameBufferAllocator *allocator = FrameBufferAllocator::create(camera_);\n+\n+\tret = capture(loop, allocator);\n \n \tif (options.isSet(OptFile)) {\n \t\tdelete writer_;\n@@ -66,17 +69,27 @@ int Capture::run(EventLoop *loop, const OptionsParser::Options &options)\n \n \tcamera_->freeBuffers();\n \n+\tdelete allocator;\n+\n \treturn ret;\n }\n \n-int Capture::capture(EventLoop *loop)\n+int Capture::capture(EventLoop *loop, FrameBufferAllocator *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());\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 +106,11 @@ 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\tconst std::vector<std::unique_ptr<FrameBuffer>> &buffers =\n+\t\t\t\tallocator->buffers(stream);\n+\t\t\tconst std::unique_ptr<FrameBuffer> &buffer = buffers[i];\n \n-\t\t\tret = request->addBuffer(stream, std::move(buffer));\n+\t\t\tret = request->addBuffer(stream, buffer.get());\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 +153,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 +166,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\tconst FrameMetadata &metadata = buffer->metadata();\n@@ -185,16 +200,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..9bca5661070efcf5 100644\n--- a/src/cam/capture.h\n+++ b/src/cam/capture.h\n@@ -10,7 +10,9 @@\n #include <chrono>\n #include <memory>\n \n+#include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n+#include <libcamera/framebuffer_allocator.h>\n #include <libcamera/request.h>\n #include <libcamera/stream.h>\n \n@@ -26,7 +28,8 @@ public:\n \n \tint run(EventLoop *loop, const OptionsParser::Options &options);\n private:\n-\tint capture(EventLoop *loop);\n+\tint capture(EventLoop *loop,\n+\t\t    libcamera::FrameBufferAllocator *allocator);\n \n \tvoid requestComplete(libcamera::Request *request);\n \ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex 20340167a140f33a..3fe40feb88be7324 100644\n--- a/src/libcamera/camera.cpp\n+++ b/src/libcamera/camera.cpp\n@@ -695,12 +695,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@@ -756,14 +750,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@@ -834,24 +820,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 \treturn pipe_->queueRequest(this, request);\n@@ -880,6 +853,13 @@ int Camera::start()\n \n \tLOG(Camera, Debug) << \"Starting capture\";\n \n+\tfor (Stream *stream : activeStreams_) {\n+\t\tif (allocator_ && !allocator_->buffers(stream).empty())\n+\t\t\tcontinue;\n+\n+\t\tpipe_->importFrameBuffers(this, stream);\n+\t}\n+\n \tint ret = pipe_->start(this);\n \tif (ret)\n \t\treturn ret;\n@@ -915,6 +895,13 @@ int Camera::stop()\n \n \tpipe_->stop(this);\n \n+\tfor (Stream *stream : activeStreams_) {\n+\t\tif (allocator_ && !allocator_->buffers(stream).empty())\n+\t\t\tcontinue;\n+\n+\t\tpipe_->freeFrameBuffers(this, stream);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -928,13 +915,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 560be3bad8d59b20..27f3852c6c87843f 100644\n--- a/src/libcamera/include/pipeline_handler.h\n+++ b/src/libcamera/include/pipeline_handler.h\n@@ -21,12 +21,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@@ -85,7 +85,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/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\nindex b73f0f0725ee2613..065f5d980b68e1cf 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,6 @@ public:\n \t\tV4L2VideoDevice *dev;\n \t\tunsigned int pad;\n \t\tstd::string name;\n-\t\tBufferPool *pool;\n \t\tstd::vector<std::unique_ptr<FrameBuffer>> buffers;\n \t};\n \n@@ -71,9 +72,7 @@ public:\n \tint configureOutput(ImgUOutput *output,\n \t\t\t    const StreamConfiguration &cfg);\n \n-\tint importOutputBuffers(ImgUOutput *output, BufferPool *pool);\n-\tint exportOutputBuffers(ImgUOutput *output, BufferPool *pool);\n-\tvoid freeBuffers();\n+\tvoid freeBuffers(IPU3CameraData *data);\n \n \tint start();\n \tint stop();\n@@ -93,9 +92,6 @@ public:\n \tImgUOutput viewfinder_;\n \tImgUOutput stat_;\n \t/* \\todo Add param video device for 3A tuning */\n-\n-\tBufferPool vfPool_;\n-\tBufferPool outPool_;\n };\n \n class CIO2Device\n@@ -156,7 +152,7 @@ public:\n \t{\n \t}\n \n-\tvoid imguOutputBufferReady(Buffer *buffer);\n+\tvoid imguOutputBufferReady(FrameBuffer *buffer);\n \tvoid imguInputBufferReady(FrameBuffer *buffer);\n \tvoid cio2BufferReady(FrameBuffer *buffer);\n \n@@ -691,39 +687,30 @@ int PipelineHandlerIPU3::allocateBuffers(Camera *camera,\n \t\tgoto error;\n \t}\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-\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+\t\tImgUDevice::ImgUOutput *output = outStream->device_;\n+\n+\t\tret = output->dev->exportBuffers(bufferCount, &output->buffers);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPU3, Error) << \"Failed to allocate ImgU \"\n+\t\t\t\t\t << output->name << \" buffers\";\n \t\t\tgoto error;\n+\t\t}\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+\t\tImgUDevice::ImgUOutput *output = vfStream->device_;\n+\n+\t\tret = output->dev->exportBuffers(bufferCount, &output->buffers);\n+\t\tif (ret < 0) {\n+\t\t\tLOG(IPU3, Error) << \"Failed to allocate ImgU \"\n+\t\t\t\t\t << output->name << \" buffers\";\n \t\t\tgoto error;\n+\t\t}\n \t}\n \n \treturn 0;\n@@ -740,7 +727,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@@ -793,7 +780,7 @@ int PipelineHandlerIPU3::queueRequestDevice(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@@ -920,9 +907,9 @@ int PipelineHandlerIPU3::registerCameras()\n \t\t\t\t\t&IPU3CameraData::cio2BufferReady);\n \t\tdata->imgu_->input_->frameBufferReady.connect(data.get(),\n \t\t\t\t\t&IPU3CameraData::imguInputBufferReady);\n-\t\tdata->imgu_->output_.dev->bufferReady.connect(data.get(),\n+\t\tdata->imgu_->output_.dev->frameBufferReady.connect(data.get(),\n \t\t\t\t\t&IPU3CameraData::imguOutputBufferReady);\n-\t\tdata->imgu_->viewfinder_.dev->bufferReady.connect(data.get(),\n+\t\tdata->imgu_->viewfinder_.dev->frameBufferReady.connect(data.get(),\n \t\t\t\t\t&IPU3CameraData::imguOutputBufferReady);\n \n \t\t/* Create and register the Camera instance. */\n@@ -971,7 +958,7 @@ void IPU3CameraData::imguInputBufferReady(FrameBuffer *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@@ -1046,7 +1033,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@@ -1056,7 +1042,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@@ -1164,69 +1149,28 @@ int ImgUDevice::configureOutput(ImgUOutput *output,\n \treturn 0;\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-{\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-\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-\n-\treturn 0;\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)\ndiff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\nindex ba8f93e8584c97a9..d669d2ded89607d2 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -51,7 +51,7 @@ struct RkISP1FrameInfo {\n \n \tFrameBuffer *paramBuffer;\n \tFrameBuffer *statBuffer;\n-\tBuffer *videoBuffer;\n+\tFrameBuffer *videoBuffer;\n \n \tbool paramFilled;\n \tbool paramDequeued;\n@@ -67,7 +67,6 @@ 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@@ -87,7 +86,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@@ -205,7 +204,7 @@ private:\n \tint initLinks();\n \tint createCamera(MediaEntity *sensor);\n \tvoid tryCompleteRequest(Request *request);\n-\tvoid bufferReady(Buffer *buffer);\n+\tvoid bufferReady(FrameBuffer *buffer);\n \tvoid paramReady(FrameBuffer *buffer);\n \tvoid statReady(FrameBuffer *buffer);\n \n@@ -243,7 +242,7 @@ RkISP1FrameInfo *RkISP1Frames::create(unsigned int frame, Request *request, Stre\n \t}\n \tFrameBuffer *statBuffer = pipe_->availableStatBuffers_.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@@ -296,26 +295,14 @@ RkISP1FrameInfo *RkISP1Frames::find(unsigned int frame)\n \treturn nullptr;\n }\n \n-RkISP1FrameInfo *RkISP1Frames::find(Buffer *buffer)\n-{\n-\tfor (auto &itInfo : frameInfo_) {\n-\t\tRkISP1FrameInfo *info = itInfo.second;\n-\n-\t\tif (info->videoBuffer == buffer)\n-\t\t\treturn info;\n-\t}\n-\n-\tLOG(RkISP1, Error) << \"Can't locate info from buffer\";\n-\treturn nullptr;\n-}\n-\n RkISP1FrameInfo *RkISP1Frames::find(FrameBuffer *buffer)\n {\n \tfor (auto &itInfo : frameInfo_) {\n \t\tRkISP1FrameInfo *info = itInfo.second;\n \n \t\tif (info->paramBuffer == buffer ||\n-\t\t    info->statBuffer == buffer)\n+\t\t    info->statBuffer == buffer ||\n+\t\t    info->videoBuffer == buffer)\n \t\t\treturn info;\n \t}\n \n@@ -692,18 +679,9 @@ 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 \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-\n \tunsigned int maxBuffers = 0;\n \tfor (const Stream *s : camera->streams())\n \t\tmaxBuffers = std::max(maxBuffers, s->configuration().bufferCount);\n@@ -769,9 +747,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@@ -975,7 +950,7 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)\n \tif (param_->open() < 0)\n \t\treturn false;\n \n-\tvideo_->bufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady);\n+\tvideo_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::bufferReady);\n \tstat_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::statReady);\n \tparam_->frameBufferReady.connect(this, &PipelineHandlerRkISP1::paramReady);\n \n@@ -1024,7 +999,7 @@ void PipelineHandlerRkISP1::tryCompleteRequest(Request *request)\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_);\ndiff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp\nindex b72841edee572f99..66380e29e1a78e13 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@@ -225,23 +225,13 @@ void PipelineHandlerUVC::freeFrameBuffers(Camera *camera, Stream *stream)\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@@ -295,7 +285,7 @@ int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)\n int PipelineHandlerUVC::queueRequestDevice(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@@ -362,7 +352,7 @@ int UVCCameraData::init(MediaEntity *entity)\n \tif (ret)\n \t\treturn ret;\n \n-\tvideo_->bufferReady.connect(this, &UVCCameraData::bufferReady);\n+\tvideo_->frameBufferReady.connect(this, &UVCCameraData::bufferReady);\n \n \t/* Initialise the supported controls. */\n \tconst ControlInfoMap &controls = video_->controls();\n@@ -402,7 +392,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 3fb5cacde0dab5b6..5e0f5c63b7fbe86a 100644\n--- a/src/libcamera/pipeline/vimc.cpp\n+++ b/src/libcamera/pipeline/vimc.cpp\n@@ -55,7 +55,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@@ -291,23 +291,13 @@ void PipelineHandlerVimc::freeFrameBuffers(Camera *camera, Stream *stream)\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@@ -355,7 +345,7 @@ int PipelineHandlerVimc::processControls(VimcCameraData *data, Request *request)\n int PipelineHandlerVimc::queueRequestDevice(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@@ -447,7 +437,7 @@ int VimcCameraData::init(MediaDevice *media)\n \tif (video_->open())\n \t\treturn -ENODEV;\n \n-\tvideo_->bufferReady.connect(this, &VimcCameraData::bufferReady);\n+\tvideo_->frameBufferReady.connect(this, &VimcCameraData::bufferReady);\n \n \traw_ = new V4L2VideoDevice(media->getEntityByName(\"Raw Capture 1\"));\n \tif (raw_->open())\n@@ -484,7 +474,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 572f751b5217eb5f..0348e3cfa68ed6ec 100644\n--- a/src/libcamera/pipeline_handler.cpp\n+++ b/src/libcamera/pipeline_handler.cpp\n@@ -467,7 +467,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 4268e31434bf4feb..ea33736fbeb56505 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@@ -106,18 +101,19 @@ Request::~Request()\n  * \\brief Retrieve the request's streams to buffers map\n  *\n  * Return a reference to the map that associates each Stream part of the\n- * request to the Buffer the Stream output should be directed to.\n+ * request to the FrameBuffer the Stream output should be directed to.\n  *\n- * \\return The map of Stream to Buffer\n+ * \\return The map of Stream to FrameBuffer\n  */\n \n /**\n- * \\brief Store a Buffer with its associated Stream in the Request\n+ * \\brief Add a FrameBuffer with its associated Stream to the Request\n  * \\param[in] stream The stream the buffer belongs to\n- * \\param[in] buffer The Buffer to store in the request\n+ * \\param[in] buffer The FrameBuffer to add to the request\n  *\n- * Ownership of the buffer is passed to the request. It will be deleted when\n- * the request is destroyed after completing.\n+ * A reference to the buffer is stored in the request. The caller is responsible\n+ * for ensuring that the buffer will remain valid until the request complete\n+ * callback is called.\n  *\n  * A request can only contain one buffer per stream. If a buffer has already\n  * been added to the request for the same stream, this method returns -EEXIST.\n@@ -126,7 +122,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@@ -135,13 +131,13 @@ int Request::addBuffer(Stream *stream, std::unique_ptr<Buffer> buffer)\n \n \tauto it = bufferMap_.find(stream);\n \tif (it != bufferMap_.end()) {\n-\t\tLOG(Request, Error) << \"Buffer already set for stream\";\n+\t\tLOG(Request, Error) << \"FrameBuffer already set for stream\";\n \t\treturn -EEXIST;\n \t}\n \n \tbuffer->request_ = this;\n-\tpending_.insert(buffer.get());\n-\tbufferMap_[stream] = buffer.release();\n+\tpending_.insert(buffer);\n+\tbufferMap_[stream] = buffer;\n \n \treturn 0;\n }\n@@ -161,7 +157,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@@ -231,7 +227,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/qcam/main_window.cpp b/src/qcam/main_window.cpp\nindex ca3464babdc1c313..701a2b9a73d53d96 100644\n--- a/src/qcam/main_window.cpp\n+++ b/src/qcam/main_window.cpp\n@@ -23,7 +23,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@@ -37,8 +37,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_ = FrameBufferAllocator::create(camera_);\n \t\tret = startCapture();\n+\t}\n \n \tif (ret < 0)\n \t\tQTimer::singleShot(0, QCoreApplication::instance(),\n@@ -49,6 +51,7 @@ MainWindow::~MainWindow()\n {\n \tif (camera_) {\n \t\tstopCapture();\n+\t\tdelete allocator_;\n \t\tcamera_->release();\n \t\tcamera_.reset();\n \t}\n@@ -176,8 +179,14 @@ int MainWindow::startCapture()\n \t\treturn ret;\n \t}\n \n+\tret = allocator_->allocate(stream);\n+\tif (ret < 0) {\n+\t\tstd::cerr << \"Failed to allocate capture 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 (const std::unique_ptr<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@@ -185,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.get());\n \t\tif (ret < 0) {\n \t\t\tstd::cerr << \"Can't set buffer for request\" << std::endl;\n \t\t\tgoto error;\n@@ -254,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 FrameMetadata &metadata = buffer->metadata();\n \n \tdouble fps = metadata.timestamp - lastBufferTime_;\n@@ -281,28 +284,21 @@ 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-\tif (buffer->mem()->planes().size() != 1)\n+\tif (buffer->planes().size() != 1)\n \t\treturn -EINVAL;\n \n \t/* \\todo Once the FrameBuffer is done cache mapped memory. */\n-\tconst FrameBuffer::Plane &plane = buffer->mem()->planes().front();\n+\tconst FrameBuffer::Plane &plane = buffer->planes().front();\n \tvoid *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED,\n \t\t\t    plane.fd.fd(), 0);\n \ndiff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\nindex 0786e915ec512255..05cde4ceab5f7ea1 100644\n--- a/src/qcam/main_window.h\n+++ b/src/qcam/main_window.h\n@@ -14,8 +14,10 @@\n #include <QObject>\n #include <QTimer>\n \n+#include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n #include <libcamera/camera_manager.h>\n+#include <libcamera/framebuffer_allocator.h>\n #include <libcamera/stream.h>\n \n #include \"../cam/options.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+\tFrameBufferAllocator *allocator_;\n+\n \tbool isCapturing_;\n \tstd::unique_ptr<CameraConfiguration> config_;\n \ndiff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp\nindex e4a03c414480f353..10db15d6276d9bf3 100644\n--- a/src/v4l2/v4l2_camera.cpp\n+++ b/src/v4l2/v4l2_camera.cpp\n@@ -16,24 +16,15 @@ using namespace libcamera;\n \n LOG_DECLARE_CATEGORY(V4L2Compat);\n \n-V4L2FrameMetadata::V4L2FrameMetadata(Buffer *buffer)\n-\t: index_(buffer->index()),\n-\t  bytesused_(buffer->metadata().planes[0].bytesused),\n-\t  timestamp_(buffer->metadata().timestamp),\n-\t  sequence_(buffer->metadata().sequence),\n-\t  status_(buffer->metadata().status)\n-{\n-}\n-\n V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)\n-\t: camera_(camera), isRunning_(false)\n+\t: camera_(camera), isRunning_(false), bufferAllocator_(nullptr)\n {\n \tcamera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);\n }\n \n V4L2Camera::~V4L2Camera()\n {\n-\tcamera_->release();\n+\tclose();\n }\n \n int V4L2Camera::open()\n@@ -50,11 +41,16 @@ int V4L2Camera::open()\n \t\treturn -EINVAL;\n \t}\n \n+\tbufferAllocator_ = FrameBufferAllocator::create(camera_);\n+\n \treturn 0;\n }\n \n void V4L2Camera::close()\n {\n+\tdelete bufferAllocator_;\n+\tbufferAllocator_ = nullptr;\n+\n \tcamera_->release();\n }\n \n@@ -63,12 +59,12 @@ void V4L2Camera::getStreamConfig(StreamConfiguration *streamConfig)\n \t*streamConfig = config_->at(0);\n }\n \n-std::vector<V4L2FrameMetadata> V4L2Camera::completedBuffers()\n+std::vector<V4L2Camera::Buffer> V4L2Camera::completedBuffers()\n {\n-\tstd::vector<V4L2FrameMetadata> v;\n+\tstd::vector<Buffer> v;\n \n \tbufferLock_.lock();\n-\tfor (std::unique_ptr<V4L2FrameMetadata> &metadata : completedBuffers_)\n+\tfor (std::unique_ptr<Buffer> &metadata : completedBuffers_)\n \t\tv.push_back(*metadata.get());\n \tcompletedBuffers_.clear();\n \tbufferLock_.unlock();\n@@ -83,9 +79,9 @@ void V4L2Camera::requestComplete(Request *request)\n \n \t/* We only have one stream at the moment. */\n \tbufferLock_.lock();\n-\tBuffer *buffer = request->buffers().begin()->second;\n-\tstd::unique_ptr<V4L2FrameMetadata> metadata =\n-\t\tutils::make_unique<V4L2FrameMetadata>(buffer);\n+\tFrameBuffer *buffer = request->buffers().begin()->second;\n+\tstd::unique_ptr<Buffer> metadata =\n+\t\tutils::make_unique<Buffer>(request->cookie(), buffer->metadata());\n \tcompletedBuffers_.push_back(std::move(metadata));\n \tbufferLock_.unlock();\n \n@@ -126,18 +122,31 @@ int V4L2Camera::configure(StreamConfiguration *streamConfigOut,\n int V4L2Camera::allocBuffers(unsigned int count)\n {\n \tint ret = camera_->allocateBuffers();\n-\treturn ret == -EACCES ? -EBUSY : ret;\n+\tif (ret)\n+\t\treturn ret == -EACCES ? -EBUSY : ret;\n+\n+\tStream *stream = *camera_->streams().begin();\n+\n+\treturn bufferAllocator_->allocate(stream);\n }\n \n void V4L2Camera::freeBuffers()\n {\n+\tStream *stream = *camera_->streams().begin();\n+\tbufferAllocator_->free(stream);\n \tcamera_->freeBuffers();\n }\n \n FileDescriptor V4L2Camera::getBufferFd(unsigned int index)\n {\n \tStream *stream = *camera_->streams().begin();\n-\treturn stream->buffers()[index].planes()[0].fd;\n+\tconst std::vector<std::unique_ptr<FrameBuffer>> &buffers =\n+\t\tbufferAllocator_->buffers(stream);\n+\n+\tif (buffers.size() <= index)\n+\t\treturn FileDescriptor();\n+\n+\treturn buffers[index]->planes()[0].fd;\n }\n \n int V4L2Camera::streamOn()\n@@ -180,21 +189,16 @@ int V4L2Camera::streamOff()\n \n int V4L2Camera::qbuf(unsigned int index)\n {\n-\tStream *stream = config_->at(0).stream();\n-\tstd::unique_ptr<Buffer> buffer = stream->createBuffer(index);\n-\tif (!buffer) {\n-\t\tLOG(V4L2Compat, Error) << \"Can't create buffer\";\n-\t\treturn -ENOMEM;\n-\t}\n-\n \tstd::unique_ptr<Request> request =\n-\t\tstd::unique_ptr<Request>(camera_->createRequest());\n+\t\tstd::unique_ptr<Request>(camera_->createRequest(index));\n \tif (!request) {\n \t\tLOG(V4L2Compat, Error) << \"Can't create request\";\n \t\treturn -ENOMEM;\n \t}\n \n-\tint ret = request->addBuffer(stream, std::move(buffer));\n+\tStream *stream = config_->at(0).stream();\n+\tFrameBuffer *buffer = bufferAllocator_->buffers(stream)[index].get();\n+\tint ret = request->addBuffer(stream, buffer);\n \tif (ret < 0) {\n \t\tLOG(V4L2Compat, Error) << \"Can't set buffer for request\";\n \t\treturn -ENOMEM;\ndiff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\nindex 06eab0e1c93b7c8a..f1f04d9ef6edf5e4 100644\n--- a/src/v4l2/v4l2_camera.h\n+++ b/src/v4l2/v4l2_camera.h\n@@ -9,50 +9,38 @@\n #define __V4L2_CAMERA_H__\n \n #include <deque>\n-#include <linux/videodev2.h>\n #include <mutex>\n+#include <utility>\n \n #include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n #include <libcamera/file_descriptor.h>\n+#include <libcamera/framebuffer_allocator.h>\n \n #include \"semaphore.h\"\n \n using namespace libcamera;\n \n-class V4L2FrameMetadata\n+class V4L2Camera : public Object\n {\n public:\n-\tV4L2FrameMetadata(Buffer *buffer);\n-\n-\tint index() const { return index_; }\n-\n-\tunsigned int bytesused() const { return bytesused_; }\n-\tuint64_t timestamp() const { return timestamp_; }\n-\tunsigned int sequence() const { return sequence_; }\n-\n-\tFrameMetadata::Status status() const { return status_; }\n-\n-private:\n-\tint index_;\n+\tstruct Buffer {\n+\t\tBuffer(unsigned int index, const FrameMetadata &data)\n+\t\t\t: index(index), data(data)\n+\t\t{\n+\t\t}\n \n-\tunsigned int bytesused_;\n-\tuint64_t timestamp_;\n-\tunsigned int sequence_;\n+\t\tunsigned int index;\n+\t\tFrameMetadata data;\n+\t};\n \n-\tFrameMetadata::Status status_;\n-};\n-\n-class V4L2Camera : public Object\n-{\n-public:\n \tV4L2Camera(std::shared_ptr<Camera> camera);\n \t~V4L2Camera();\n \n \tint open();\n \tvoid close();\n \tvoid getStreamConfig(StreamConfiguration *streamConfig);\n-\tstd::vector<V4L2FrameMetadata> completedBuffers();\n+\tstd::vector<Buffer> completedBuffers();\n \n \tint configure(StreamConfiguration *streamConfigOut,\n \t\t      const Size &size, PixelFormat pixelformat,\n@@ -78,9 +66,10 @@ private:\n \tbool isRunning_;\n \n \tstd::mutex bufferLock_;\n+\tFrameBufferAllocator *bufferAllocator_;\n \n \tstd::deque<std::unique_ptr<Request>> pendingRequests_;\n-\tstd::deque<std::unique_ptr<V4L2FrameMetadata>> completedBuffers_;\n+\tstd::deque<std::unique_ptr<Buffer>> completedBuffers_;\n };\n \n #endif /* __V4L2_CAMERA_H__ */\ndiff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp\nindex 59cf360edd28f69c..d76570a926cf667b 100644\n--- a/src/v4l2/v4l2_camera_proxy.cpp\n+++ b/src/v4l2/v4l2_camera_proxy.cpp\n@@ -187,17 +187,18 @@ void V4L2CameraProxy::querycap(std::shared_ptr<Camera> camera)\n \n void V4L2CameraProxy::updateBuffers()\n {\n-\tstd::vector<V4L2FrameMetadata> completedBuffers = vcam_->completedBuffers();\n-\tfor (V4L2FrameMetadata &fmd : completedBuffers) {\n-\t\tstruct v4l2_buffer &buf = buffers_[fmd.index()];\n+\tstd::vector<V4L2Camera::Buffer> completedBuffers = vcam_->completedBuffers();\n+\tfor (const V4L2Camera::Buffer &buffer : completedBuffers) {\n+\t\tconst FrameMetadata &fmd = buffer.data;\n+\t\tstruct v4l2_buffer &buf = buffers_[buffer.index];\n \n-\t\tswitch (fmd.status()) {\n+\t\tswitch (fmd.status) {\n \t\tcase FrameMetadata::FrameSuccess:\n-\t\t\tbuf.bytesused = fmd.bytesused();\n+\t\t\tbuf.bytesused = fmd.planes[0].bytesused;\n \t\t\tbuf.field = V4L2_FIELD_NONE;\n-\t\t\tbuf.timestamp.tv_sec = fmd.timestamp() / 1000000000;\n-\t\t\tbuf.timestamp.tv_usec = fmd.timestamp() % 1000000;\n-\t\t\tbuf.sequence = fmd.sequence();\n+\t\t\tbuf.timestamp.tv_sec = fmd.timestamp / 1000000000;\n+\t\t\tbuf.timestamp.tv_usec = fmd.timestamp % 1000000;\n+\t\t\tbuf.sequence = fmd.sequence;\n \n \t\t\tbuf.flags |= V4L2_BUF_FLAG_DONE;\n \t\t\tbreak;\ndiff --git a/test/camera/buffer_import.cpp b/test/camera/buffer_import.cpp\nindex 327db7912c09ed2d..f506d1b221e568ad 100644\n--- a/test/camera/buffer_import.cpp\n+++ b/test/camera/buffer_import.cpp\n@@ -120,7 +120,7 @@ public:\n \t}\n \n protected:\n-\tvoid bufferComplete(Request *request, Buffer *buffer)\n+\tvoid bufferComplete(Request *request, FrameBuffer *buffer)\n \t{\n \t\tif (buffer->metadata().status != FrameMetadata::FrameSuccess)\n \t\t\treturn;\n@@ -133,17 +133,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\tint dmabuf = buffers.begin()->second->dmabufs()[0];\n-\t\tstd::unique_ptr<Buffer> buffer = stream->createBuffer({ dmabuf, -1, -1 });\n+\t\tFrameBuffer *buffer = buffers.begin()->second;\n \n \t\trequest = camera_->createRequest();\n-\t\trequest->addBuffer(stream, std::move(buffer));\n+\t\trequest->addBuffer(stream, buffer);\n \t\tcamera_->queueRequest(request);\n \t}\n \n@@ -158,9 +157,6 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n-\t\tStreamConfiguration &cfg = config_->at(0);\n-\t\tcfg.memoryType = ExternalMemory;\n-\n \t\treturn TestPass;\n \t}\n \n@@ -191,17 +187,14 @@ protected:\n \t\t\treturn TestFail;\n \n \t\tstd::vector<Request *> requests;\n-\t\tfor (const std::unique_ptr<FrameBuffer> &framebuffer : source.buffers()) {\n-\t\t\tint dmabuf = framebuffer->planes()[0].fd.fd();\n-\n+\t\tfor (const std::unique_ptr<FrameBuffer> &buffer : source.buffers()) {\n \t\t\tRequest *request = camera_->createRequest();\n \t\t\tif (!request) {\n \t\t\t\tstd::cout << \"Failed to create request\" << std::endl;\n \t\t\t\treturn TestFail;\n \t\t\t}\n \n-\t\t\tstd::unique_ptr<Buffer> buffer = stream->createBuffer({ dmabuf, -1, -1 });\n-\t\t\tif (request->addBuffer(stream, std::move(buffer))) {\n+\t\t\tif (request->addBuffer(stream, buffer.get())) {\n \t\t\t\tstd::cout << \"Failed to associating buffer with request\" << std::endl;\n \t\t\t\treturn TestFail;\n \t\t\t}\ndiff --git a/test/camera/capture.cpp b/test/camera/capture.cpp\nindex 0d9ffc476650f414..de879ee4eb1420a6 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->metadata().status != FrameMetadata::FrameSuccess)\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@@ -64,9 +63,16 @@ protected:\n \t\t\treturn TestFail;\n \t\t}\n \n+\t\tallocator_ = FrameBufferAllocator::create(camera_);\n+\n \t\treturn TestPass;\n \t}\n \n+\tvoid cleanup() override\n+\t{\n+\t\tdelete allocator_;\n+\t}\n+\n \tint run() override\n \t{\n \t\tStreamConfiguration &cfg = config_->at(0);\n@@ -87,21 +93,20 @@ protected:\n \t\t}\n \n \t\tStream *stream = cfg.stream();\n+\n+\t\tint ret = allocator_->allocate(stream);\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 (const std::unique_ptr<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.get())) {\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 +139,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 \n@@ -160,6 +167,7 @@ protected:\n \t}\n \n \tstd::unique_ptr<CameraConfiguration> config_;\n+\tFrameBufferAllocator *allocator_;\n };\n \n } /* namespace */\ndiff --git a/test/camera/statemachine.cpp b/test/camera/statemachine.cpp\nindex f627b8f37422350e..f3a7ca7c32a5ec97 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_ = FrameBufferAllocator::create(camera_);\n+\t\tStream *stream = *camera_->streams().begin();\n+\t\tif (allocator_->allocate(stream) < 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].get()))\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+\tFrameBufferAllocator *allocator_;\n };\n \n } /* namespace */\n",
    "prefixes": [
        "libcamera-devel",
        "v4",
        "26/32"
    ]
}