diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
index 0b95e41a2dd205b2..862031123b4b510c 100644
--- a/include/libcamera/buffer.h
+++ b/include/libcamera/buffer.h
@@ -11,6 +11,8 @@
 #include <stdint.h>
 #include <vector>
 
+#include <libcamera/file_descriptor.h>
+
 namespace libcamera {
 
 class Request;
@@ -33,6 +35,36 @@ struct FrameMetadata {
 	std::vector<Plane> planes;
 };
 
+class FrameBuffer final
+{
+public:
+	struct Plane {
+		FileDescriptor fd;
+		unsigned int length;
+	};
+
+	FrameBuffer(const std::vector<Plane> &planes = {}, unsigned int cookie = 0);
+
+	const std::vector<Plane> &planes() const { return planes_; }
+
+	Request *request() const { return request_; }
+	const FrameMetadata &metadata() const { return metadata_; };
+
+	unsigned int cookie() const { return cookie_; }
+	void setCookie(unsigned int cookie) { cookie_ = cookie; }
+
+private:
+	friend class Request; /* Needed to update request_. */
+	friend class V4L2VideoDevice; /* Needed to update metadata_. */
+
+	std::vector<Plane> planes_;
+
+	Request *request_;
+	FrameMetadata metadata_;
+
+	unsigned int cookie_;
+};
+
 class Plane final
 {
 public:
diff --git a/src/libcamera/buffer.cpp b/src/libcamera/buffer.cpp
index 7673b96f29728a24..7c78b9b8f1e90aa5 100644
--- a/src/libcamera/buffer.cpp
+++ b/src/libcamera/buffer.cpp
@@ -229,7 +229,7 @@ void *Plane::mem()
 }
 
 /**
- * \fn Plane::length()
+ * \fn Plane::length() const
  * \brief Retrieve the length of the memory region
  * \return The length of the memory region
  */
@@ -462,4 +462,134 @@ void Buffer::cancel()
  * The intended callers are Request::prepare() and Request::completeBuffer().
  */
 
+/**
+ * \class FrameBuffer
+ * \brief A buffer handle and dynamic metadata
+ *
+ * The FrameBuffer class is the primary interface for applications, IPAs and
+ * pipelines to interact with capture memory. It contains all static and
+ * dynamic information to handle the whole life-cycle of creating, queueing,
+ * capturing and consumption of a single frame capture.
+ *
+ * The static information describes the one or more memory planes which makes
+ * up the complete frame. The static information needs to be available at
+ * creation of the FrameBuffer and needs to be expressed as a valid dmabuf file
+ * descriptor and length.
+ *
+ * The dynamic data, content of the memory planes and the metadata, is updated
+ * each time the buffer is successfully processed by a camera, by queueing it as
+ * part of a request. The dynamic data is only valid after the cameras buffer
+ * complete signal have been raised and until the FrameBuffer is queued once
+ * more to the camera as part of a new request.
+ *
+ * The creator of a FrameBuffer (applications, IPA or pipeline) may associate
+ * an integer cookie with the instance at any time to aid in its usage by the
+ * object creator. This cookie is transparent to libcamera core and shall only
+ * be set and consumed by the user. This supplements the Request objects cookie.
+ */
+
+/**
+ * \struct FrameBuffer::Plane
+ * \brief A description of a memory plane
+ *
+ * A memory plane is described by a dmabuf file descriptor and a plane length.
+ *
+ * Applications or IPAs can use this information to map the planes memory and
+ * access its content. The mapper of the memory is responsible for unmaping
+ * the memory before the FrameBuffer::Plane object is destroyed.
+ *
+ * \todo Once we have a Kernel API which can express offsets within a plane
+ * this structure shall be extended to contain this information.
+ */
+
+/**
+ * \var FrameBuffer::Plane::fd
+ * \brief The dmabuf file descriptor
+ */
+
+/**
+ * \var FrameBuffer::Plane::length
+ *
+ * The length value is only used if the memory of the plane needs to be mapped.
+ * Therefor a length of zero is valid for planes allocated outside libcamera and
+ * queued to a Camera as part of a Request, libcamera will never poke inside
+ * that memory and thus do not need to know its length.
+ *
+ * All planes created by pipeline handlers however need to have a correct length
+ * set as IPAs and other parts of libcamera core might wish to memory map the
+ * plane.
+ *
+ * \brief The plane length
+ */
+
+/**
+ * \brief Create a libcamera FrameBuffer object from an array of planes
+ * \param[in] planes Planes that makes up the FrameBuffer
+ * \param[in] cookie Integer cookie
+ *
+ * Create a FrameBuffer object from an array of static plane descriptions. The
+ * creator may close the file descriptors in the plane description after the
+ * FrameBuffer have been created.
+ */
+FrameBuffer::FrameBuffer(const std::vector<Plane> &planes, unsigned int cookie)
+	: request_(nullptr), cookie_(cookie)
+{
+	/* Clone all file descriptors. */
+	planes_ = planes;
+}
+
+/**
+ * \fn FrameBuffer::planes()
+ * \brief Retrieve the static plane descriptions
+ * \return Array of plane descriptions
+ */
+
+/**
+ * \fn FrameBuffer::request()
+ * \brief Retrieve the request this buffer belongs to
+ *
+ * The intended callers of this method are buffer completion handlers that
+ * need to associate a buffer to the request it belongs to.
+ *
+ * A Buffer is associated to a request by Request::prepare() and the
+ * association is valid until the buffer completes. The returned request
+ * pointer is valid only during that interval.
+ *
+ * \return The Request the Buffer belongs to, or nullptr if the buffer is
+ * either completed or not associated with a request
+ */
+
+/**
+ * \fn FrameBuffer::metadata()
+ * \brief Retrieve the dynamic metadata
+ * \return Dynamic metadata for the frame
+ */
+
+/**
+ * \fn FrameBuffer::cookie()
+ * \brief Retrieve the integer cookie
+ *
+ * The cookie shall only be read by the creator of the FrameBuffer. The cookie
+ * is transparent to libcamera core.
+ *
+ * \return Integer cookie
+ */
+
+/**
+ * \fn FrameBuffer::setCookie()
+ * \brief Set the integer cookie
+ * \param[in] cookie Cookie to set
+ *
+ * The cookie shall only be set by the creator of the FrameBuffer. The cookie
+ * is transparent to libcamera core.
+ */
+
+/**
+ * \var FrameBuffer::request_
+ * \brief The request this frame belongs to
+ *
+ * This member is intended to be set by Request::prepare() and
+ * Request::completeBuffer().
+ */
+
 } /* namespace libcamera */
