Show a patch.

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

{
    "id": 2271,
    "url": "https://patchwork.libcamera.org/api/patches/2271/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/2271/",
    "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": "<20191028022525.796995-12-niklas.soderlund@ragnatech.se>",
    "date": "2019-10-28T02:25:24",
    "name": "[libcamera-devel,RFC,11/12] libcamera: buffer: Switch to new buffer API",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "8171ad074a3668b47db6deb50a87fd131f2e6f67",
    "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/2271/mbox/",
    "series": [
        {
            "id": 561,
            "url": "https://patchwork.libcamera.org/api/series/561/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=561",
            "date": "2019-10-28T02:25:13",
            "name": "libcamera: Rework buffer API",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/561/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/2271/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/2271/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<niklas.soderlund@ragnatech.se>",
        "Received": [
            "from bin-mail-out-05.binero.net (bin-mail-out-05.binero.net\n\t[195.74.38.228])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id EB3CF6017C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 28 Oct 2019 03:26:01 +0100 (CET)",
            "from localhost.localdomain (unknown [93.2.121.143])\n\tby bin-vsp-out-02.atm.binero.net (Halon) with ESMTPA\n\tid 44f25a5d-f92a-11e9-903a-005056917f90;\n\tMon, 28 Oct 2019 03:25:55 +0100 (CET)"
        ],
        "X-Halon-ID": "44f25a5d-f92a-11e9-903a-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": "Mon, 28 Oct 2019 03:25:24 +0100",
        "Message-Id": "<20191028022525.796995-12-niklas.soderlund@ragnatech.se>",
        "X-Mailer": "git-send-email 2.23.0",
        "In-Reply-To": "<20191028022525.796995-1-niklas.soderlund@ragnatech.se>",
        "References": "<20191028022525.796995-1-niklas.soderlund@ragnatech.se>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [RFC 11/12] libcamera: buffer: Switch to new\n\tbuffer API",
        "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": "Mon, 28 Oct 2019 02:26:02 -0000"
    },
    "content": "Switch to the new buffer API where all buffers are treated as external\nbuffers and are allocated outside the camera. For V4L2 backed cameras a\nhelper class BufferAllocator is provided to help applications create\nbuffers.\n\nThis patch is quiet large as it need to touch most areas of libcamera to\nswap the API. It also restores the buffer sharing test and functionality\nwhich was broken in the previous change. A follow up change to this one\nwill finalize the transition to the new API by removing code that is\nleft unused after this change.\n\nSigned-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n---\n include/ipa/ipa_interface.h                   |   2 +-\n include/libcamera/buffer.h                    |  47 +--\n include/libcamera/request.h                   |   2 +-\n src/cam/buffer_writer.cpp                     |   7 +-\n src/cam/capture.cpp                           |  32 ++-\n src/cam/capture.h                             |   3 +-\n src/ipa/rkisp1/rkisp1.cpp                     |  14 +-\n src/libcamera/buffer.cpp                      | 243 +++-------------\n src/libcamera/camera.cpp                      |  47 +--\n src/libcamera/include/v4l2_videodevice.h      |  33 ++-\n src/libcamera/pipeline/ipu3/ipu3.cpp          | 199 ++++---------\n src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  74 ++---\n src/libcamera/pipeline/uvcvideo.cpp           |  16 +-\n src/libcamera/pipeline/vimc.cpp               |  16 +-\n src/libcamera/request.cpp                     |  15 +-\n src/libcamera/v4l2_videodevice.cpp            | 267 ++++++++++--------\n src/qcam/main_window.cpp                      |  43 ++-\n src/qcam/main_window.h                        |   3 +\n test/camera/capture.cpp                       |  29 +-\n test/camera/statemachine.cpp                  |  12 +-\n test/v4l2_videodevice/buffer_sharing.cpp      |  21 +-\n test/v4l2_videodevice/capture_async.cpp       |  12 +-\n test/v4l2_videodevice/request_buffers.cpp     |  11 +-\n test/v4l2_videodevice/stream_on_off.cpp       |   6 +-\n test/v4l2_videodevice/v4l2_m2mdevice.cpp      |  36 +--\n test/v4l2_videodevice/v4l2_videodevice_test.h |   2 +-\n 26 files changed, 435 insertions(+), 757 deletions(-)",
    "diff": "diff --git a/include/ipa/ipa_interface.h b/include/ipa/ipa_interface.h\nindex 8fd658af5fdb2e6b..efb86a33f6f427ae 100644\n--- a/include/ipa/ipa_interface.h\n+++ b/include/ipa/ipa_interface.h\n@@ -26,7 +26,7 @@ struct IPAStream {\n \n struct IPABuffer {\n \tunsigned int id;\n-\tBufferMemory memory;\n+\tstd::vector<std::pair<int, unsigned int>> planes;\n };\n \n struct IPAOperationData {\ndiff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h\nindex adb642ad5da072d2..3568c2e0cdf8d95b 100644\n--- a/include/libcamera/buffer.h\n+++ b/include/libcamera/buffer.h\n@@ -22,11 +22,10 @@ class Stream;\n class Plane final\n {\n public:\n-\tPlane();\n+\tPlane(int fd, unsigned int length);\n \t~Plane();\n \n-\tint dmabuf() const { return fd_; }\n-\tint setDmabuf(int fd, unsigned int length);\n+\tint fd() const { return fd_; }\n \n \tvoid *mem();\n \tunsigned int length() const { return length_; }\n@@ -67,45 +66,17 @@ private:\n class Buffer final\n {\n public:\n-\tenum Status {\n-\t\tBufferSuccess,\n-\t\tBufferError,\n-\t\tBufferCancelled,\n-\t};\n-\n-\tBuffer(unsigned int index = -1, const Buffer *metadata = nullptr);\n-\tBuffer(const Buffer &) = delete;\n-\tBuffer &operator=(const Buffer &) = delete;\n+\tBuffer(std::vector<std::pair<int, unsigned int>> planes);\n+\t~Buffer();\n \n-\tunsigned int index() const { return index_; }\n-\tconst std::array<int, 3> &dmabufs() const { return dmabuf_; }\n-\tBufferMemory *mem() { return mem_; }\n-\n-\tunsigned int bytesused() const { return bytesused_; }\n-\tuint64_t timestamp() const { return timestamp_; }\n-\tunsigned int sequence() const { return sequence_; }\n+\tunsigned int numPlanes() { return planes_.size(); }\n+\tPlane *plane(unsigned int plane);\n \n-\tStatus status() const { return status_; }\n-\tStream *stream() const { return stream_; }\n+\tstd::array<int, 3> fds() const;\n+\tstd::vector<std::pair<int, unsigned int>> description() const;\n \n private:\n-\tfriend class Camera;\n-\tfriend class Request;\n-\tfriend class Stream;\n-\tfriend class V4L2VideoDevice;\n-\n-\tvoid cancel();\n-\n-\tunsigned int index_;\n-\tstd::array<int, 3> dmabuf_;\n-\tBufferMemory *mem_;\n-\n-\tunsigned int bytesused_;\n-\tuint64_t timestamp_;\n-\tunsigned int sequence_;\n-\n-\tStatus status_;\n-\tStream *stream_;\n+\tstd::vector<Plane *> planes_;\n };\n \n class BufferInfo\ndiff --git a/include/libcamera/request.h b/include/libcamera/request.h\nindex 88ef7bf03fcfb77b..ac2beab46bea6466 100644\n--- a/include/libcamera/request.h\n+++ b/include/libcamera/request.h\n@@ -40,7 +40,7 @@ public:\n \tControlList &metadata() { return *metadata_; }\n \tconst std::map<Stream *, Buffer *> &buffers() const { return bufferMap_; }\n \tconst BufferInfo &info(Buffer *frame) const { return info_.find(frame)->second; };\n-\tint addBuffer(std::unique_ptr<Buffer> buffer);\n+\tint addBuffer(Stream *stream, Buffer *buffer);\n \tBuffer *findBuffer(Stream *stream) const;\n \n \tuint64_t cookie() const { return cookie_; }\ndiff --git a/src/cam/buffer_writer.cpp b/src/cam/buffer_writer.cpp\nindex 3ee9e82ba216abb6..c9df851f3ecec056 100644\n--- a/src/cam/buffer_writer.cpp\n+++ b/src/cam/buffer_writer.cpp\n@@ -43,10 +43,9 @@ int BufferWriter::write(Buffer *buffer, const BufferInfo &info,\n \tif (fd == -1)\n \t\treturn -errno;\n \n-\tBufferMemory *mem = buffer->mem();\n-\tfor (Plane &plane : mem->planes()) {\n-\t\tvoid *data = plane.mem();\n-\t\tunsigned int length = plane.length();\n+\tfor (unsigned int i = 0; i < buffer->numPlanes(); i++) {\n+\t\tvoid *data = buffer->plane(i)->mem();\n+\t\tunsigned int length = buffer->plane(i)->length();\n \n \t\tret = ::write(fd, data, length);\n \t\tif (ret < 0) {\ndiff --git a/src/cam/capture.cpp b/src/cam/capture.cpp\nindex 251e9f86c86b508d..8bd77dcec2d15f1c 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,21 @@ 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());\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\tnbuffers = std::min(nbuffers, static_cast<unsigned int>(ret));\n+\t}\n \n \t/*\n \t * TODO: make cam tool smarter to support still capture by for\n@@ -93,9 +103,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\tBuffer *buffer = allocator.get(stream)[i];\n \n-\t\t\tret = request->addBuffer(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@@ -179,15 +189,7 @@ void Capture::requestComplete(Request *request)\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 \" << index << std::endl;\n-\t\t\treturn;\n-\t\t}\n-\n-\t\trequest->addBuffer(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..ee5adad7cbaaae47 100644\n--- a/src/cam/capture.h\n+++ b/src/cam/capture.h\n@@ -10,6 +10,7 @@\n #include <chrono>\n #include <memory>\n \n+#include <libcamera/buffer.h>\n #include <libcamera/camera.h>\n #include <libcamera/request.h>\n #include <libcamera/stream.h>\n@@ -26,7 +27,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/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp\nindex 9a13f5c7df17f335..42d3379ce84d600a 100644\n--- a/src/ipa/rkisp1/rkisp1.cpp\n+++ b/src/ipa/rkisp1/rkisp1.cpp\n@@ -47,7 +47,7 @@ private:\n \tvoid setControls(unsigned int frame);\n \tvoid metadataReady(unsigned int frame, unsigned int aeState);\n \n-\tstd::map<unsigned int, BufferMemory> bufferInfo_;\n+\tstd::map<unsigned int, Buffer *> bufferInfo_;\n \n \tControlInfoMap ctrls_;\n \n@@ -101,15 +101,17 @@ void IPARkISP1::configure(const std::map<unsigned int, IPAStream> &streamConfig,\n void IPARkISP1::mapBuffers(const std::vector<IPABuffer> &buffers)\n {\n \tfor (const IPABuffer &buffer : buffers) {\n-\t\tbufferInfo_[buffer.id] = buffer.memory;\n-\t\tbufferInfo_[buffer.id].planes()[0].mem();\n+\t\tbufferInfo_[buffer.id] = new Buffer(buffer.planes);\n+\t\tbufferInfo_[buffer.id]->plane(0)->mem();\n \t}\n }\n \n void IPARkISP1::unmapBuffers(const std::vector<unsigned int> &ids)\n {\n-\tfor (unsigned int id : ids)\n+\tfor (unsigned int id : ids) {\n+\t\tdelete bufferInfo_[id];\n \t\tbufferInfo_.erase(id);\n+\t}\n }\n \n void IPARkISP1::processEvent(const IPAOperationData &event)\n@@ -120,7 +122,7 @@ void IPARkISP1::processEvent(const IPAOperationData &event)\n \t\tunsigned int bufferId = event.data[1];\n \n \t\tconst rkisp1_stat_buffer *stats =\n-\t\t\tstatic_cast<rkisp1_stat_buffer *>(bufferInfo_[bufferId].planes()[0].mem());\n+\t\t\tstatic_cast<rkisp1_stat_buffer *>(bufferInfo_[bufferId]->plane(0)->mem());\n \n \t\tupdateStatistics(frame, stats);\n \t\tbreak;\n@@ -130,7 +132,7 @@ void IPARkISP1::processEvent(const IPAOperationData &event)\n \t\tunsigned int bufferId = event.data[1];\n \n \t\trkisp1_isp_params_cfg *params =\n-\t\t\tstatic_cast<rkisp1_isp_params_cfg *>(bufferInfo_[bufferId].planes()[0].mem());\n+\t\t\tstatic_cast<rkisp1_isp_params_cfg *>(bufferInfo_[bufferId]->plane(0)->mem());\n \n \t\tqueueRequest(frame, params, event.controls[0]);\n \t\tbreak;\ndiff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp\nindex fce1ce5e49cbbf42..d00849520bc2b51c 100644\n--- a/src/libcamera/buffer.cpp\n+++ b/src/libcamera/buffer.cpp\n@@ -48,69 +48,44 @@ LOG_DEFINE_CATEGORY(Buffer)\n  * an image may or may not be contiguous.\n  */\n \n-Plane::Plane()\n-\t: fd_(-1), length_(0), mem_(0)\n-{\n-}\n-\n-Plane::~Plane()\n-{\n-\tmunmap();\n-\n-\tif (fd_ != -1)\n-\t\tclose(fd_);\n-}\n-\n-/**\n- * \\fn Plane::dmabuf()\n- * \\brief Get the dmabuf file handle backing the buffer\n- */\n-\n /**\n  * \\brief Set the dmabuf file handle backing the buffer\n  * \\param[in] fd The dmabuf file handle\n  * \\param[in] length The size of the memory region\n  *\n- * The \\a fd dmabuf file handle is duplicated and stored. The caller may close\n- * the original file handle.\n- *\n- * \\return 0 on success or a negative error code otherwise\n+ * The \\a fd dmabuf file handle is duplicated and stored.\n  */\n-int Plane::setDmabuf(int fd, unsigned int length)\n+Plane::Plane(int fd, unsigned int length)\n+\t: fd_(-1), length_(length), mem_(nullptr)\n {\n \tif (fd < 0) {\n-\t\tLOG(Buffer, Error) << \"Invalid dmabuf fd provided\";\n-\t\treturn -EINVAL;\n-\t}\n-\n-\tif (fd_ != -1) {\n-\t\tclose(fd_);\n-\t\tfd_ = -1;\n+\t\tLOG(Buffer, Fatal) << \"Invalid dmabuf fd provided\";\n+\t\treturn;\n \t}\n \n \tfd_ = dup(fd);\n+\n \tif (fd_ == -1) {\n \t\tint ret = -errno;\n-\t\tLOG(Buffer, Error)\n+\t\tLOG(Buffer, Fatal)\n \t\t\t<< \"Failed to duplicate dmabuf: \" << strerror(-ret);\n-\t\treturn ret;\n+\t\treturn;\n \t}\n+}\n \n-\tlength_ = length;\n+Plane::~Plane()\n+{\n+\tmunmap();\n \n-\treturn 0;\n+\tif (fd_ != -1)\n+\t\tclose(fd_);\n }\n \n /**\n- * \\brief Map the plane memory data to a CPU accessible address\n- *\n- * The file descriptor to map the memory from must be set by a call to\n- * setDmaBuf() before calling this function.\n- *\n- * \\sa setDmaBuf()\n- *\n- * \\return 0 on success or a negative error code otherwise\n+ * \\fn Plane::fd()\n+ * \\brief Get the dmabuf file handle backing the buffer\n  */\n+\n int Plane::mmap()\n {\n \tvoid *map;\n@@ -131,13 +106,6 @@ int Plane::mmap()\n \treturn 0;\n }\n \n-/**\n- * \\brief Unmap any existing CPU accessible mapping\n- *\n- * Unmap the memory mapped by an earlier call to mmap().\n- *\n- * \\return 0 on success or a negative error code otherwise\n- */\n int Plane::munmap()\n {\n \tint ret = 0;\n@@ -236,166 +204,49 @@ void BufferPool::destroyBuffers()\n  * \\return A vector containing all the buffers in the pool.\n  */\n \n-/**\n- * \\class Buffer\n- * \\brief A buffer handle and dynamic metadata\n- *\n- * The Buffer class references a buffer memory and associates dynamic metadata\n- * related to the frame contained in the buffer. It allows referencing buffer\n- * memory through a single interface regardless of whether the memory is\n- * allocated internally in libcamera or provided externally through dmabuf.\n- *\n- * Buffer instances are allocated dynamically for a stream through\n- * Stream::createBuffer(), added to a request with Request::addBuffer() and\n- * deleted automatically after the request complete handler returns.\n- */\n-\n-/**\n- * \\enum Buffer::Status\n- * Buffer completion status\n- * \\var Buffer::BufferSuccess\n- * The buffer has completed with success and contains valid data. All its other\n- * metadata (such as bytesused(), timestamp() or sequence() number) are valid.\n- * \\var Buffer::BufferError\n- * The buffer has completed with an error and doesn't contain valid data. Its\n- * other metadata are valid.\n- * \\var Buffer::BufferCancelled\n- * The buffer has been cancelled due to capture stop. Its other metadata are\n- * invalid and shall not be used.\n- */\n-\n-/**\n- * \\brief Construct a buffer not associated with any stream\n- *\n- * This method constructs an orphaned buffer not associated with any stream. It\n- * is not meant to be called by applications, they should instead create buffers\n- * for a stream with Stream::createBuffer().\n- */\n-Buffer::Buffer(unsigned int index, const Buffer *metadata)\n-\t: index_(index), dmabuf_({ -1, -1, -1 }),\n-\t  status_(Buffer::BufferSuccess), stream_(nullptr)\n+Buffer::Buffer(std::vector<std::pair<int, unsigned int>> planes)\n {\n-\tif (metadata) {\n-\t\tbytesused_ = metadata->bytesused_;\n-\t\tsequence_ = metadata->sequence_;\n-\t\ttimestamp_ = metadata->timestamp_;\n-\t} else {\n-\t\tbytesused_ = 0;\n-\t\tsequence_ = 0;\n-\t\ttimestamp_ = 0;\n-\t}\n+\tfor (std::pair<int, unsigned int> plane : planes)\n+\t\tplanes_.push_back(new Plane(plane.first, plane.second));\n }\n \n-/**\n- * \\fn Buffer::index()\n- * \\brief Retrieve the Buffer index\n- * \\return The buffer index\n- */\n+Buffer::~Buffer()\n+{\n+\tfor (Plane *plane : planes_)\n+\t\tdelete plane;\n+}\n \n-/**\n- * \\fn Buffer::dmabufs()\n- * \\brief Retrieve the dmabuf file descriptors for all buffer planes\n- *\n- * The dmabufs array contains one dmabuf file descriptor per plane. Unused\n- * entries are set to -1.\n- *\n- * \\return The dmabuf file descriptors\n- */\n+Plane *Buffer::plane(unsigned int plane)\n+{\n+\tif (plane > planes_.size()) {\n+\t\tLOG(Buffer, Error) << \"Plane \" << plane << \" is out of range\";\n+\t\treturn nullptr;\n+\t}\n \n-/**\n- * \\fn Buffer::mem()\n- * \\brief Retrieve the BufferMemory this buffer is associated with\n- *\n- * The association between the buffer and a BufferMemory instance is valid from\n- * the time the request containing this buffer is queued to a camera to the end\n- * of that request's completion handler.\n- *\n- * \\return The BufferMemory this buffer is associated with\n- */\n+\treturn planes_[plane];\n+}\n \n-/**\n- * \\fn Buffer::bytesused()\n- * \\brief Retrieve the number of bytes occupied by the data in the buffer\n- * \\return Number of bytes occupied in the buffer\n- */\n+std::array<int, 3> Buffer::fds() const\n+{\n+\tstd::array<int, 3> ret = { -1, -1, -1 };\n \n-/**\n- * \\fn Buffer::timestamp()\n- * \\brief Retrieve the time when the buffer was processed\n- *\n- * The timestamp is expressed as a number of nanoseconds since the epoch.\n- *\n- * \\return Timestamp when the buffer was processed\n- */\n+\tunsigned int i = 0;\n+\tfor (const Plane *plane : planes_)\n+\t\tret[i++] = plane->fd();\n \n-/**\n- * \\fn Buffer::sequence()\n- * \\brief Retrieve the buffer sequence number\n- *\n- * The sequence number is a monotonically increasing number assigned to the\n- * buffer processed by the stream. Gaps in the sequence numbers indicate\n- * dropped frames.\n- *\n- * \\return Sequence number of the buffer\n- */\n-\n-/**\n- * \\fn Buffer::status()\n- * \\brief Retrieve the buffer status\n- *\n- * The buffer status reports whether the buffer has completed successfully\n- * (BufferSuccess) or if an error occurred (BufferError).\n- *\n- * \\return The buffer status\n- */\n+\treturn ret;\n+}\n \n-/**\n- * \\fn Buffer::request()\n- * \\brief Retrieve the request this buffer belongs to\n- *\n- * The intended callers of this method are buffer completion handlers that\n- * need to associate a buffer to the request it belongs to.\n- *\n- * A Buffer is associated to a request by Request::prepare() and the\n- * association is valid until the buffer completes. The returned request\n- * pointer is valid only during that interval.\n- *\n- * \\return The Request the Buffer belongs to, or nullptr if the buffer is\n- * either completed or not associated with a request\n- * \\sa Buffer::setRequest()\n- */\n+std::vector<std::pair<int, unsigned int>> Buffer::description() const\n+{\n+\tstd::vector<std::pair<int, unsigned int>> desc;\n \n-/**\n- * \\fn Buffer::stream()\n- * \\brief Retrieve the stream this buffer is associated with\n- *\n- * A Buffer is associated to the stream that created it with\n- * Stream::createBuffer() and the association is valid until the buffer is\n- * destroyed. Buffer instances that are created directly are not associated\n- * with any stream.\n- *\n- * \\return The Stream the Buffer is associated with, or nullptr if the buffer\n- * is not associated with a stream\n- */\n+\tfor (const Plane *plane : planes_)\n+\t\tdesc.emplace_back(plane->fd(), plane->length());\n \n-/**\n- * \\brief Mark a buffer as cancel by setting its status to BufferCancelled\n- */\n-void Buffer::cancel()\n-{\n-\tbytesused_ = 0;\n-\ttimestamp_ = 0;\n-\tsequence_ = 0;\n-\tstatus_ = BufferCancelled;\n+\treturn desc;\n }\n \n-/**\n- * \\fn Buffer::setRequest()\n- * \\brief Set the request this buffer belongs to\n- *\n- * The intended callers are Request::prepare() and Request::completeBuffer().\n- */\n-\n BufferAllocator::BufferAllocator(std::shared_ptr<Camera> camera)\n \t: camera_(camera)\n {\ndiff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\nindex e810fb725d81350d..63944c0199338f39 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@@ -856,15 +829,23 @@ int Camera::queueRequest(Request *request)\n  */\n int Camera::start()\n {\n+\tint ret;\n+\n \tif (disconnected_)\n \t\treturn -ENODEV;\n \n \tif (!stateIs(CameraPrepared))\n \t\treturn -EACCES;\n \n+\tfor (Stream *stream : streams_) {\n+\t\tret = stream->importBuffers(true);\n+\t\tif (ret < 0)\n+\t\t\treturn ret;\n+\t}\n+\n \tLOG(Camera, Debug) << \"Starting capture\";\n \n-\tint ret = pipe_->start(this);\n+\tret = pipe_->start(this);\n \tif (ret)\n \t\treturn ret;\n \n@@ -899,6 +880,9 @@ int Camera::stop()\n \n \tpipe_->stop(this);\n \n+\tfor (Stream *stream : streams_)\n+\t\tstream->importBuffers(false);\n+\n \treturn 0;\n }\n \n@@ -912,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/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h\nindex 01b90ab4654bfba2..cbbb6cd955bf527f 100644\n--- a/src/libcamera/include/v4l2_videodevice.h\n+++ b/src/libcamera/include/v4l2_videodevice.h\n@@ -14,7 +14,9 @@\n \n #include <libcamera/buffer.h>\n #include <libcamera/geometry.h>\n+#include <libcamera/request.h>\n #include <libcamera/signal.h>\n+#include <libcamera/stream.h>\n \n #include \"formats.h\"\n #include \"log.h\"\n@@ -22,9 +24,6 @@\n \n namespace libcamera {\n \n-class Buffer;\n-class BufferMemory;\n-class BufferPool;\n class EventNotifier;\n class MediaDevice;\n class MediaEntity;\n@@ -161,12 +160,11 @@ public:\n \tint setFormat(V4L2DeviceFormat *format);\n \tImageFormats formats();\n \n-\tint exportBuffers(BufferPool *pool);\n-\tint importBuffers(BufferPool *pool);\n+\tint allocateBuffers(unsigned int count, std::vector<Buffer *> *buffers);\n+\tint importBuffers(unsigned int count);\n \tint releaseBuffers();\n \n-\tint queueBuffer(Buffer *buffer);\n-\tstd::vector<std::unique_ptr<Buffer>> queueAllBuffers();\n+\tint queueBuffer(Buffer *buffer, const BufferInfo *info = nullptr);\n \tSignal<Buffer *, const BufferInfo &> bufferReady;\n \n \tint streamOn();\n@@ -192,8 +190,8 @@ private:\n \tstd::vector<SizeRange> enumSizes(unsigned int pixelFormat);\n \n \tint requestBuffers(unsigned int count);\n-\tint createPlane(BufferMemory *buffer, unsigned int index,\n-\t\t\tunsigned int plane, unsigned int length);\n+\tBuffer *createBuffer(struct v4l2_buffer buf);\n+\tint expbuf(unsigned int index, unsigned int plane);\n \n \tvoid bufferAvailable(EventNotifier *notifier);\n \n@@ -202,7 +200,7 @@ private:\n \tenum v4l2_buf_type bufferType_;\n \tenum v4l2_memory memoryType_;\n \n-\tBufferPool *bufferPool_;\n+\tV4L2BufferCache *cache_;\n \tstd::map<unsigned int, Buffer *> queuedBuffers_;\n \n \tEventNotifier *fdEvent_;\n@@ -227,6 +225,21 @@ private:\n \tV4L2VideoDevice *capture_;\n };\n \n+class V4L2Stream : public Stream\n+{\n+public:\n+\tV4L2Stream(V4L2VideoDevice *video, unsigned int bufferCount);\n+\n+protected:\n+\tint allocateBuffers(std::vector<Buffer *> *buffers) override;\n+\tint importBuffers(bool enable) override;\n+\n+private:\n+\tV4L2VideoDevice *video_;\n+\tunsigned int bufferCount_;\n+\tbool allocated;\n+};\n+\n } /* namespace libcamera */\n \n #endif /* __LIBCAMERA_V4L2_VIDEODEVICE_H__ */\ndiff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\nindex 01064ac09859155d..af8e93c04bc40f04 100644\n--- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n@@ -43,7 +43,7 @@ public:\n \t\tV4L2VideoDevice *dev;\n \t\tunsigned int pad;\n \t\tstd::string name;\n-\t\tBufferPool *pool;\n+\t\tstd::vector<Buffer *> buffers;\n \t};\n \n \tImgUDevice()\n@@ -69,9 +69,8 @@ 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+\tint importInputBuffers(unsigned int count);\n+\tint exportOutputBuffers(ImgUOutput *output, unsigned int count);\n \tvoid freeBuffers();\n \n \tint start();\n@@ -92,10 +91,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@@ -119,10 +114,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@@ -131,14 +126,17 @@ public:\n \tV4L2Subdevice *csi2_;\n \tCameraSensor *sensor_;\n \n-\tBufferPool pool_;\n+private:\n+\tstd::vector<Buffer *> buffers_;\n };\n \n-class IPU3Stream : public Stream\n+class IPU3Stream : public V4L2Stream\n {\n public:\n-\tIPU3Stream(ImgUDevice::ImgUOutput *device, const std::string &name)\n-\t\t: active_(false), name_(name), device_(device)\n+\tIPU3Stream(ImgUDevice::ImgUOutput *device, const std::string &name,\n+\t\t   unsigned int count)\n+\t\t: V4L2Stream(device->dev, count), active_(false), name_(name),\n+\t\t  device_(device)\n \t{\n \t}\n \n@@ -170,8 +168,6 @@ public:\n \n \tIPU3Stream *outStream_;\n \tIPU3Stream *vfStream_;\n-\n-\tstd::vector<std::unique_ptr<Buffer>> rawBuffers_;\n };\n \n class IPU3CameraConfiguration : public CameraConfiguration\n@@ -296,8 +292,6 @@ void IPU3CameraConfiguration::adjustStream(StreamConfiguration &cfg, bool scale)\n \t\tcfg.size.width &= ~7;\n \t\tcfg.size.height &= ~3;\n \t}\n-\n-\tcfg.bufferCount = IPU3_BUFFER_COUNT;\n }\n \n CameraConfiguration::Status IPU3CameraConfiguration::validate()\n@@ -635,70 +629,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@@ -727,7 +693,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@@ -743,7 +709,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@@ -757,8 +722,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::queueRequest(Camera *camera, Request *request)\n@@ -880,8 +843,8 @@ int PipelineHandlerIPU3::registerCameras()\n \t\t * second.\n \t\t */\n \t\tdata->imgu_ = numCameras ? &imgu1_ : &imgu0_;\n-\t\tdata->outStream_ = new IPU3Stream(&data->imgu_->output_, \"output\");\n-\t\tdata->vfStream_ = new IPU3Stream(&data->imgu_->viewfinder_, \"viewfinder\");\n+\t\tdata->outStream_ = new IPU3Stream(&data->imgu_->output_, \"output\", 4);\n+\t\tdata->vfStream_ = new IPU3Stream(&data->imgu_->viewfinder_, \"viewfinder\", 4);\n \n \t\t/*\n \t\t * Connect video devices' 'bufferReady' signals to their\n@@ -1019,7 +982,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@@ -1029,7 +991,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@@ -1038,7 +999,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@@ -1138,65 +1098,25 @@ 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_->importBuffers(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@@ -1451,38 +1371,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 (Buffer *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 (Buffer *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 33a058de18b8cf2e..fb224370de19303d 100644\n--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp\n@@ -31,8 +31,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@@ -153,8 +152,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@@ -213,9 +210,6 @@ 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 \n@@ -508,8 +502,6 @@ CameraConfiguration::Status RkISP1CameraConfiguration::validate()\n \t\tstatus = Adjusted;\n \t}\n \n-\tcfg.bufferCount = RKISP1_BUFFER_COUNT;\n-\n \treturn status;\n }\n \n@@ -660,46 +652,40 @@ 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<Buffer *> paramBuffers, statBuffers;\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-\tparamPool_.createBuffers(stream->configuration().bufferCount + 1);\n-\tret = param_->exportBuffers(&paramPool_);\n-\tif (ret) {\n-\t\tvideo_->releaseBuffers();\n-\t\treturn ret;\n-\t}\n+\tret = param_->allocateBuffers(RKISP1_BUFFER_COUNT, &paramBuffers);\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-\t}\n+\tret = stat_->allocateBuffers(RKISP1_BUFFER_COUNT, &statBuffers);\n+\tif (ret < 0)\n+\t\tgoto err;\n \n-\tfor (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) {\n-\t\tdata->ipaBuffers_.push_back({ .id = RKISP1_PARAM_BASE | i,\n-\t\t\t\t\t      .memory = paramPool_.buffers()[i] });\n-\t\tparamBuffers_.push(new Buffer(i));\n+\tfor (Buffer *buffer : paramBuffers) {\n+\t\tdata->ipaBuffers_.push_back({ .id = static_cast<unsigned int>(buffer->plane(0)->fd()),\n+\t\t\t\t\t      .planes = buffer->description() });\n+\t\tparamBuffers_.push(buffer);\n \t}\n \n-\tfor (unsigned int i = 0; i < stream->configuration().bufferCount + 1; i++) {\n-\t\tdata->ipaBuffers_.push_back({ .id = RKISP1_STAT_BASE | i,\n-\t\t\t\t\t      .memory = statPool_.buffers()[i] });\n-\t\tstatBuffers_.push(new Buffer(i));\n+\tfor (Buffer *buffer : statBuffers) {\n+\t\tdata->ipaBuffers_.push_back({ .id = static_cast<unsigned int>(buffer->plane(0)->fd()),\n+\t\t\t\t\t      .planes = buffer->description() });\n+\t\tstatBuffers_.push(buffer);\n \t}\n \n \tdata->ipa_->mapBuffers(data->ipaBuffers_);\n \n+\treturn 0;\n+err:\n+\tfor (Buffer *buffer : paramBuffers)\n+\t\tdelete buffer;\n+\n+\tfor (Buffer *buffer : statBuffers)\n+\t\tdelete buffer;\n+\n \treturn ret;\n }\n \n@@ -820,9 +806,11 @@ int PipelineHandlerRkISP1::queueRequest(Camera *camera, Request *request)\n \tif (!info)\n \t\treturn -ENOENT;\n \n+\tunsigned int paramid = static_cast<unsigned int>(info->paramBuffer->plane(0)->fd());\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@@ -874,7 +862,7 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)\n \tstd::unique_ptr<RkISP1CameraData> data =\n \t\tutils::make_unique<RkISP1CameraData>(this);\n \n-\tdata->stream_ = new Stream();\n+\tdata->stream_ = new V4L2Stream(video_, RKISP1_BUFFER_COUNT);\n \n \tControlInfoMap::Map ctrls;\n \tctrls.emplace(std::piecewise_construct,\n@@ -982,9 +970,9 @@ 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, const BufferInfo &info)\n@@ -1023,7 +1011,7 @@ void PipelineHandlerRkISP1::statReady(Buffer *buffer, const BufferInfo &info)\n \t\treturn;\n \n \tunsigned int frame = rkinfo->frame;\n-\tunsigned int statid = RKISP1_STAT_BASE | rkinfo->statBuffer->index();\n+\tunsigned int statid = static_cast<unsigned int>(rkinfo->statBuffer->plane(0)->fd());\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 ef2e8c9734f844ce..21e594075eefffdc 100644\n--- a/src/libcamera/pipeline/uvcvideo.cpp\n+++ b/src/libcamera/pipeline/uvcvideo.cpp\n@@ -136,8 +136,6 @@ CameraConfiguration::Status UVCCameraConfiguration::validate()\n \t\tstatus = Adjusted;\n \t}\n \n-\tcfg.bufferCount = 4;\n-\n \treturn status;\n }\n \n@@ -161,7 +159,6 @@ CameraConfiguration *PipelineHandlerUVC::generateConfiguration(Camera *camera,\n \n \tcfg.pixelFormat = formats.pixelformats().front();\n \tcfg.size = formats.sizes(cfg.pixelFormat).back();\n-\tcfg.bufferCount = 4;\n \n \tconfig->addConfiguration(cfg);\n \n@@ -196,16 +193,7 @@ 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@@ -331,7 +319,7 @@ int UVCCameraData::init(MediaEntity *entity)\n \tif (ret)\n \t\treturn ret;\n \n-\tstream_ = new Stream();\n+\tstream_ = new V4L2Stream(video_, 4);\n \n \tvideo_->bufferReady.connect(this, &UVCCameraData::bufferReady);\n \ndiff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp\nindex e3eefc49135179f2..a14b7c8834174a5f 100644\n--- a/src/libcamera/pipeline/vimc.cpp\n+++ b/src/libcamera/pipeline/vimc.cpp\n@@ -158,8 +158,6 @@ CameraConfiguration::Status VimcCameraConfiguration::validate()\n \t\tstatus = Adjusted;\n \t}\n \n-\tcfg.bufferCount = 4;\n-\n \treturn status;\n }\n \n@@ -190,7 +188,6 @@ CameraConfiguration *PipelineHandlerVimc::generateConfiguration(Camera *camera,\n \n \tcfg.pixelFormat = V4L2_PIX_FMT_RGB24;\n \tcfg.size = { 1920, 1080 };\n-\tcfg.bufferCount = 4;\n \n \tconfig->addConfiguration(cfg);\n \n@@ -263,16 +260,7 @@ 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@@ -419,7 +407,7 @@ int VimcCameraData::init(MediaDevice *media)\n \tif (video_->open())\n \t\treturn -ENODEV;\n \n-\tstream_ = new Stream();\n+\tstream_ = new V4L2Stream(video_, 4);\n \n \tvideo_->bufferReady.connect(this, &VimcCameraData::bufferReady);\n \ndiff --git a/src/libcamera/request.cpp b/src/libcamera/request.cpp\nindex 9a0e439a3d05d780..2bda7a57e8d6381e 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@@ -125,21 +120,15 @@ 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(std::unique_ptr<Buffer> buffer)\n+int Request::addBuffer(Stream *stream, Buffer *buffer)\n {\n-\tStream *stream = buffer->stream();\n-\tif (!stream) {\n-\t\tLOG(Request, Error) << \"Invalid stream reference\";\n-\t\treturn -EINVAL;\n-\t}\n-\n \tauto it = bufferMap_.find(stream);\n \tif (it != bufferMap_.end()) {\n \t\tLOG(Request, Error) << \"Buffer already set for stream\";\n \t\treturn -EEXIST;\n \t}\n \n-\tbufferMap_[stream] = buffer.release();\n+\tbufferMap_[stream] = buffer;\n \n \treturn 0;\n }\ndiff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\nindex 97c6722b8c4c98cf..873452c9608fc9f1 100644\n--- a/src/libcamera/v4l2_videodevice.cpp\n+++ b/src/libcamera/v4l2_videodevice.cpp\n@@ -19,6 +19,7 @@\n \n #include <libcamera/buffer.h>\n #include <libcamera/event_notifier.h>\n+#include <libcamera/stream.h>\n \n #include \"log.h\"\n #include \"media_device.h\"\n@@ -319,7 +320,7 @@ const std::string V4L2DeviceFormat::toString() const\n  * \\param[in] deviceNode The file-system path to the video device node\n  */\n V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)\n-\t: V4L2Device(deviceNode), bufferPool_(nullptr), fdEvent_(nullptr)\n+\t: V4L2Device(deviceNode), cache_(nullptr), fdEvent_(nullptr)\n {\n \t/*\n \t * We default to an MMAP based CAPTURE video device, however this will\n@@ -879,27 +880,32 @@ int V4L2VideoDevice::requestBuffers(unsigned int count)\n }\n \n /**\n- * \\brief Request buffers to be allocated from the video device and stored in\n- * the buffer pool provided.\n- * \\param[out] pool BufferPool to populate with buffers\n+ * \\brief Operate using buffers allocated from local video device\n+ * \\param[in] count Number of buffers to allocate\n+ * \\param[out] buffer Vector to store local buffers\n  * \\return 0 on success or a negative error code otherwise\n  */\n-int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n+int V4L2VideoDevice::allocateBuffers(unsigned int count, std::vector<Buffer *> *buffers)\n {\n \tunsigned int i;\n \tint ret;\n \n+\tif (cache_) {\n+\t\tLOG(V4L2, Error) << \"Can't export buffers with existing cache\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tcache_ = new V4L2BufferCache(count);\n+\n \tmemoryType_ = V4L2_MEMORY_MMAP;\n \n-\tret = requestBuffers(pool->count());\n-\tif (ret)\n-\t\treturn ret;\n+\tret = requestBuffers(count);\n+\tif (ret < 0)\n+\t\tgoto err_cache;\n \n-\t/* Map the buffers. */\n-\tfor (i = 0; i < pool->count(); ++i) {\n-\t\tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n+\tfor (i = 0; i < count; ++i) {\n \t\tstruct v4l2_buffer buf = {};\n-\t\tBufferMemory &buffer = pool->buffers()[i];\n+\t\tstruct v4l2_plane planes[VIDEO_MAX_PLANES] = {};\n \n \t\tbuf.index = i;\n \t\tbuf.type = bufferType_;\n@@ -912,51 +918,71 @@ int V4L2VideoDevice::exportBuffers(BufferPool *pool)\n \t\t\tLOG(V4L2, Error)\n \t\t\t\t<< \"Unable to query buffer \" << i << \": \"\n \t\t\t\t<< strerror(-ret);\n-\t\t\tbreak;\n+\t\t\tgoto err_buf;\n \t\t}\n \n-\t\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {\n-\t\t\tfor (unsigned int p = 0; p < buf.length; ++p) {\n-\t\t\t\tret = createPlane(&buffer, i, p,\n-\t\t\t\t\t\t  buf.m.planes[p].length);\n-\t\t\t\tif (ret)\n-\t\t\t\t\tbreak;\n-\t\t\t}\n-\t\t} else {\n-\t\t\tret = createPlane(&buffer, i, 0, buf.length);\n+\t\tBuffer *buffer = createBuffer(buf);\n+\t\tif (!buffer) {\n+\t\t\tLOG(V4L2, Error) << \"Unable to create buffer\";\n+\t\t\tret = -EINVAL;\n+\t\t\tgoto err_buf;\n \t\t}\n \n-\t\tif (ret) {\n-\t\t\tLOG(V4L2, Error) << \"Failed to create plane\";\n-\t\t\tbreak;\n-\t\t}\n+\t\tbuffers->push_back(buffer);\n+\t\tcache_->populate(i, buffer->fds());\n \t}\n \n-\tif (ret) {\n-\t\trequestBuffers(0);\n-\t\tpool->destroyBuffers();\n-\t\treturn ret;\n+\treturn count;\n+err_buf:\n+\trequestBuffers(0);\n+\n+\tfor (Buffer *buffer : *buffers)\n+\t\tdelete buffer;\n+\n+\tbuffers->clear();\n+err_cache:\n+\tdelete cache_;\n+\tcache_ = nullptr;\n+\n+\treturn ret;\n+}\n+\n+Buffer *V4L2VideoDevice::createBuffer(struct v4l2_buffer buf)\n+{\n+\tconst unsigned int numPlanes = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ? buf.length : 1;\n+\tstd::vector<std::pair<int, unsigned int>> planes;\n+\tBuffer *frame = nullptr;\n+\n+\tfor (unsigned int plane = 0; plane < numPlanes; plane++) {\n+\t\tint fd = expbuf(buf.index, plane);\n+\t\tif (fd < 0)\n+\t\t\tgoto out;\n+\n+\t\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type))\n+\t\t\tplanes.emplace_back(fd, buf.m.planes[plane].length);\n+\t\telse\n+\t\t\tplanes.emplace_back(fd, buf.length);\n \t}\n \n-\tbufferPool_ = pool;\n+\tif (!planes.empty())\n+\t\tframe = new Buffer(planes);\n+\telse\n+\t\tLOG(V4L2, Error) << \"Failed to get planes\";\n+out:\n+\tfor (std::pair<int, unsigned int> plane : planes)\n+\t\t::close(plane.first);\n \n-\treturn 0;\n+\treturn frame;\n }\n \n-int V4L2VideoDevice::createPlane(BufferMemory *buffer, unsigned int index,\n-\t\t\t\t unsigned int planeIndex, unsigned int length)\n+int V4L2VideoDevice::expbuf(unsigned int index, unsigned int plane)\n {\n \tstruct v4l2_exportbuffer expbuf = {};\n \tint ret;\n \n-\tLOG(V4L2, Debug)\n-\t\t<< \"Buffer \" << index\n-\t\t<< \" plane \" << planeIndex\n-\t\t<< \": length=\" << length;\n-\n \texpbuf.type = bufferType_;\n \texpbuf.index = index;\n-\texpbuf.plane = planeIndex;\n+\texpbuf.plane = plane;\n \texpbuf.flags = O_RDWR;\n \n \tret = ioctl(VIDIOC_EXPBUF, &expbuf);\n@@ -966,31 +992,31 @@ int V4L2VideoDevice::createPlane(BufferMemory *buffer, unsigned int index,\n \t\treturn ret;\n \t}\n \n-\tbuffer->planes().emplace_back();\n-\tPlane &plane = buffer->planes().back();\n-\tplane.setDmabuf(expbuf.fd, length);\n-\t::close(expbuf.fd);\n-\n-\treturn 0;\n+\treturn expbuf.fd;\n }\n \n /**\n- * \\brief Import the externally allocated \\a pool of buffers\n- * \\param[in] pool BufferPool of buffers to import\n+ * \\brief Operate using buffers imported from external source\n+ * \\param[in] count Number of buffers to prepare for\n  * \\return 0 on success or a negative error code otherwise\n  */\n-int V4L2VideoDevice::importBuffers(BufferPool *pool)\n+int V4L2VideoDevice::importBuffers(unsigned int count)\n {\n+\t/* Only prepare for external buffers if internals not in use. */\n+\tif (cache_)\n+\t\treturn 0;\n+\n \tint ret;\n \n \tmemoryType_ = V4L2_MEMORY_DMABUF;\n \n-\tret = requestBuffers(pool->count());\n+\tret = requestBuffers(count);\n \tif (ret)\n \t\treturn ret;\n \n-\tLOG(V4L2, Debug) << \"provided pool of \" << pool->count() << \" buffers\";\n-\tbufferPool_ = pool;\n+\tcache_ = new V4L2BufferCache(count);\n+\n+\tLOG(V4L2, Debug) << \"provided for \" << count << \" external buffers\";\n \n \treturn 0;\n }\n@@ -1000,9 +1026,10 @@ int V4L2VideoDevice::importBuffers(BufferPool *pool)\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@@ -1018,40 +1045,42 @@ 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(Buffer *buffer, const BufferInfo *info)\n {\n-\tstruct v4l2_plane v4l2Planes[VIDEO_MAX_PLANES] = {};\n \tstruct v4l2_buffer buf = {};\n+\tstruct v4l2_plane v4l2Planes[VIDEO_MAX_PLANES] = {};\n \tint ret;\n \n-\tbuf.index = buffer->index();\n+\tASSERT(cache_);\n+\n+\tbuf.index = cache_->pop(buffer->fds());\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<Plane> &planes = mem->planes();\n+\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {\n+\t\tbuf.length = buffer->numPlanes();\n+\t\tbuf.m.planes = v4l2Planes;\n+\t}\n \n \tif (buf.memory == V4L2_MEMORY_DMABUF) {\n-\t\tif (multiPlanar) {\n-\t\t\tfor (unsigned int p = 0; p < planes.size(); ++p)\n-\t\t\t\tv4l2Planes[p].m.fd = planes[p].dmabuf();\n-\t\t} else {\n-\t\t\tbuf.m.fd = planes[0].dmabuf();\n-\t\t}\n+\t\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type))\n+\t\t\tfor (unsigned int i = 0; i < buffer->numPlanes(); i++)\n+\t\t\t\tv4l2Planes[i].m.fd = buffer->plane(i)->fd();\n+\t\telse\n+\t\t\tbuf.m.fd = buffer->plane(0)->fd();\n \t}\n \n-\tif (multiPlanar) {\n-\t\tbuf.length = planes.size();\n-\t\tbuf.m.planes = v4l2Planes;\n-\t}\n+\tif (V4L2_TYPE_IS_OUTPUT(bufferType_) && info) {\n+\t\tif (V4L2_TYPE_IS_MULTIPLANAR(buf.type))\n+\t\t\tfor (unsigned int i = 0; i < buffer->numPlanes(); i++)\n+\t\t\t\tv4l2Planes[i].bytesused = info->plane(i).bytesused;\n+\t\telse\n+\t\t\tbuf.bytesused = info->plane(0).bytesused;\n \n-\tif (V4L2_TYPE_IS_OUTPUT(bufferType_)) {\n-\t\tbuf.bytesused = buffer->bytesused_;\n-\t\tbuf.sequence = buffer->sequence_;\n-\t\tbuf.timestamp.tv_sec = buffer->timestamp_ / 1000000000;\n-\t\tbuf.timestamp.tv_usec = (buffer->timestamp_ / 1000) % 1000000;\n+\t\tbuf.sequence = info->sequence();\n+\t\tbuf.timestamp.tv_sec = info->timestamp() / 1000000000;\n+\t\tbuf.timestamp.tv_usec = (info->timestamp() / 1000) % 1000000;\n \t}\n \n \tLOG(V4L2, Debug) << \"Queueing buffer \" << buf.index;\n@@ -1072,52 +1101,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 Slot to handle completed buffer events from the V4L2 video device\n  * \\param[in] notifier The event notifier\n@@ -1149,6 +1132,7 @@ void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n \t\treturn;\n \t}\n \n+\tcache_->push(buf.index);\n \tLOG(V4L2, Debug) << \"Buffer \" << buf.index << \" is available\";\n \n \tauto it = queuedBuffers_.find(buf.index);\n@@ -1164,7 +1148,6 @@ void V4L2VideoDevice::bufferAvailable(EventNotifier *notifier)\n \t\t+ buf.timestamp.tv_usec * 1000ULL;\n \n \tBufferInfo info(status, buf.sequence, timestamp, { { buf.bytesused } });\n-\tbuffer->index_ = buf.index;\n \n \t/* Notify anyone listening to the device. */\n \tbufferReady.emit(buffer, info);\n@@ -1216,13 +1199,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 \n \t\tBufferInfo info(BufferInfo::BufferCancelled, 0, 0, { {} });\n \n-\t\tbuffer->index_ = index;\n-\t\tbuffer->cancel();\n \t\tbufferReady.emit(buffer, info);\n \t}\n \n@@ -1353,4 +1333,41 @@ void V4L2M2MDevice::close()\n \toutput_->close();\n }\n \n+V4L2Stream::V4L2Stream(V4L2VideoDevice *video, unsigned int bufferCount)\n+\t: Stream(), video_(video), bufferCount_(bufferCount),\n+\t  allocated(false)\n+{\n+}\n+\n+int V4L2Stream::allocateBuffers(std::vector<Buffer *> *buffers)\n+{\n+\tif (buffers) {\n+\t\tif (allocated)\n+\t\t\treturn -EINVAL;\n+\n+\t\tallocated = true;\n+\t\treturn video_->allocateBuffers(bufferCount_, buffers);\n+\t} else {\n+\t\tif (!allocated)\n+\t\t\treturn -EINVAL;\n+\n+\t\treturn video_->releaseBuffers();\n+\t}\n+}\n+\n+int V4L2Stream::importBuffers(bool enable)\n+{\n+\t/*\n+\t * No need to prepare for buffer importing if internal buffers\n+\t * are allocated.\n+\t */\n+\tif (allocated)\n+\t\treturn 0;\n+\n+\tif (enable)\n+\t\treturn video_->importBuffers(bufferCount_);\n+\telse\n+\t\treturn video_->releaseBuffers();\n+}\n+\n } /* namespace libcamera */\ndiff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp\nindex 2d7f4ba84fbb6838..d17aec42c82bbd98 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,22 +179,24 @@ 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 internal buffers\" << std::endl;\n+\t\treturn ret;\n+\t}\n+\tunsigned int nbuffers = static_cast<unsigned int>(ret);\n+\n \tstd::vector<Request *> requests;\n-\tfor (unsigned int i = 0; i < cfg.bufferCount; ++i) {\n+\tfor (unsigned int i = 0; i < nbuffers; ++i) {\n \t\tRequest *request = camera_->createRequest();\n-\t\tif (!request) {\n+\t\tBuffer *buffer = allocator_->get(stream)[i];\n+\t\tif (!request || !buffer) {\n \t\t\tstd::cerr << \"Can't create request\" << std::endl;\n \t\t\tret = -ENOMEM;\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(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@@ -281,15 +287,8 @@ void MainWindow::requestComplete(Request *request)\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 \" << index << std::endl;\n-\t\t\treturn;\n-\t\t}\n \n-\t\trequest->addBuffer(std::move(newBuffer));\n+\t\trequest->addBuffer(stream, buffer);\n \t}\n \n \tcamera_->queueRequest(request);\n@@ -297,12 +296,10 @@ void MainWindow::requestComplete(Request *request)\n \n int MainWindow::display(Buffer *buffer, const BufferInfo &info)\n {\n-\tBufferMemory *mem = buffer->mem();\n-\tif (mem->planes().size() != 1)\n+\tif (buffer->numPlanes() != 1)\n \t\treturn -EINVAL;\n \n-\tPlane &plane = mem->planes().front();\n-\tunsigned char *raw = static_cast<unsigned char *>(plane.mem());\n+\tunsigned char *raw = static_cast<unsigned char *>(buffer->plane(0)->mem());\n \tviewfinder_->display(raw, info.plane(0).bytesused);\n \n \treturn 0;\ndiff --git a/src/qcam/main_window.h b/src/qcam/main_window.h\nindex 8cbd72eb0d63cbea..eeb28eaae0dfc135 100644\n--- a/src/qcam/main_window.h\n+++ b/src/qcam/main_window.h\n@@ -15,6 +15,7 @@\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/stream.h>\n@@ -58,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/capture.cpp b/test/camera/capture.cpp\nindex 3cf5b798448d01db..8d3d612222a3b21e 100644\n--- a/test/camera/capture.cpp\n+++ b/test/camera/capture.cpp\n@@ -41,10 +41,9 @@ protected:\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 \n \t\trequest = camera_->createRequest();\n-\t\trequest->addBuffer(std::move(newBuffer));\n+\t\trequest->addBuffer(stream, buffer);\n \t\tcamera_->queueRequest(request);\n \t}\n \n@@ -84,21 +83,25 @@ protected:\n \t\t}\n \n \t\tStream *stream = cfg.stream();\n+\n+\t\tBufferAllocator allocator(camera_);\n+\t\tint ret = allocator.allocate(stream);\n+\t\tif (ret < 0)\n+\t\t\treturn TestFail;\n+\n+\t\tunsigned int nbuffers = static_cast<unsigned int>(ret);\n+\n \t\tstd::vector<Request *> requests;\n-\t\tfor (unsigned int i = 0; i < cfg.bufferCount; ++i) {\n+\t\tfor (unsigned int i = 0; i < nbuffers; ++i) {\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+\t\t\tBuffer *buffer = allocator.get(stream)[i];\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\tif (!request || !buffer) {\n+\t\t\t\tcout << \"Failed to create request\" << endl;\n \t\t\t\treturn TestFail;\n \t\t\t}\n \n-\t\t\tif (request->addBuffer(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@@ -131,10 +134,10 @@ protected:\n \t\twhile (timer.isRunning())\n \t\t\tdispatcher->processEvents();\n \n-\t\tif (completeRequestsCount_ <= cfg.bufferCount * 2) {\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 12d5e0e1d5343f28..a77dec0f8b986634 100644\n--- a/test/camera/statemachine.cpp\n+++ b/test/camera/statemachine.cpp\n@@ -178,6 +178,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) < 0)\n+\t\t\treturn TestFail;\n+\n \t\tif (camera_->start())\n \t\t\treturn TestFail;\n \n@@ -211,8 +217,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(std::move(buffer)))\n+\t\tif (request->addBuffer(stream, allocator_->get(stream)[0]))\n \t\t\treturn TestFail;\n \n \t\tif (camera_->queueRequest(request))\n@@ -222,6 +227,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@@ -278,6 +285,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 3609c331cf10e056..f0c4ace2f47cc58b 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_->importBuffers(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@@ -98,8 +96,7 @@ protected:\n \t\tif (info.status() != BufferInfo::BufferSuccess)\n \t\t\treturn;\n \n-\t\tBufferInfo infocopy = info;\n-\t\toutput_->queueBuffer(buffer);\n+\t\toutput_->queueBuffer(buffer, &info);\n \t\tframesCaptured_++;\n \t}\n \n@@ -124,10 +121,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 (Buffer *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 26ea1d17fd901ef8..a12c968f36e937ab 100644\n--- a/test/v4l2_videodevice/capture_async.cpp\n+++ b/test/v4l2_videodevice/capture_async.cpp\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 (Buffer *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 e6ca90a4604dfcda..def8d82972d754f9 100644\n--- a/test/v4l2_videodevice/v4l2_m2mdevice.cpp\n+++ b/test/v4l2_videodevice/v4l2_m2mdevice.cpp\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 (Buffer *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 (Buffer *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<Buffer *> captureBuffers_;\n+\tstd::vector<Buffer *> 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..1b6f4b862fbae020 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<Buffer *> buffers_;\n };\n \n #endif /* __LIBCAMERA_V4L2_DEVICE_TEST_H_ */\n",
    "prefixes": [
        "libcamera-devel",
        "RFC",
        "11/12"
    ]
}