diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
index 254f8797af42dd8a..f4cbcfbd26ea540c 100644
--- a/src/libcamera/include/v4l2_videodevice.h
+++ b/src/libcamera/include/v4l2_videodevice.h
@@ -162,6 +162,8 @@ public:
 
 	int exportBuffers(BufferPool *pool);
 	int importBuffers(BufferPool *pool);
+	int allocateBuffers(unsigned int count, std::vector<FrameBuffer *> *buffers);
+	int externalBuffers(unsigned int count);
 	int releaseBuffers();
 
 	int queueBuffer(Buffer *buffer);
@@ -197,6 +199,7 @@ private:
 	int requestBuffers(unsigned int count);
 	int createPlane(BufferMemory *buffer, unsigned int index,
 			unsigned int plane, unsigned int length);
+	FrameBuffer *createBuffer(struct v4l2_buffer buf);
 	int exportDmaBuffer(unsigned int index, unsigned int plane);
 
 	Buffer *dequeueBuffer();
@@ -208,6 +211,7 @@ private:
 	enum v4l2_memory memoryType_;
 
 	BufferPool *bufferPool_;
+	V4L2BufferCache *cache_;
 	std::map<unsigned int, Buffer *> queuedBuffers_;
 
 	EventNotifier *fdEvent_;
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index c82f2829601bd14c..9fe66018ec502626 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -377,7 +377,8 @@ const std::string V4L2DeviceFormat::toString() const
  * \param[in] deviceNode The file-system path to the video device node
  */
 V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)
-	: V4L2Device(deviceNode), bufferPool_(nullptr), fdEvent_(nullptr)
+	: V4L2Device(deviceNode), bufferPool_(nullptr), cache_(nullptr),
+	  fdEvent_(nullptr)
 {
 	/*
 	 * We default to an MMAP based CAPTURE video device, however this will
@@ -1042,6 +1043,100 @@ int V4L2VideoDevice::importBuffers(BufferPool *pool)
 	return 0;
 }
 
+/**
+ * \brief Operate using buffers allocated from local video device
+ * \param[in] count Number of buffers to allocate
+ * \param[out] buffers Vector to store local buffers
+ * \return 0 on success or a negative error code otherwise
+ */
+int V4L2VideoDevice::allocateBuffers(unsigned int count, std::vector<FrameBuffer *> *buffers)
+{
+	unsigned int i;
+	int ret;
+
+	if (cache_) {
+		LOG(V4L2, Error) << "Can't allocate buffers when cache exists";
+		return -EINVAL;
+	}
+
+	memoryType_ = V4L2_MEMORY_MMAP;
+
+	ret = requestBuffers(count);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < count; ++i) {
+		struct v4l2_buffer buf = {};
+		struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
+
+		buf.index = i;
+		buf.type = bufferType_;
+		buf.memory = memoryType_;
+		buf.length = VIDEO_MAX_PLANES;
+		buf.m.planes = planes;
+
+		ret = ioctl(VIDIOC_QUERYBUF, &buf);
+		if (ret < 0) {
+			LOG(V4L2, Error)
+				<< "Unable to query buffer " << i << ": "
+				<< strerror(-ret);
+			goto err_buf;
+		}
+
+		FrameBuffer *buffer = createBuffer(buf);
+		if (!buffer) {
+			LOG(V4L2, Error) << "Unable to create buffer";
+			ret = -EINVAL;
+			goto err_buf;
+		}
+
+		buffers->push_back(buffer);
+	}
+
+	cache_ = new V4L2BufferCache(*buffers);
+
+	return count;
+err_buf:
+	requestBuffers(0);
+
+	for (FrameBuffer *buffer : *buffers)
+		delete buffer;
+
+	buffers->clear();
+
+	return ret;
+}
+
+FrameBuffer *V4L2VideoDevice::createBuffer(struct v4l2_buffer buf)
+{
+	const unsigned int numPlanes = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ? buf.length : 1;
+	std::vector<FrameBuffer::Plane> planes;
+	FrameBuffer *frame = nullptr;
+
+	for (unsigned int nplane = 0; nplane < numPlanes; nplane++) {
+		int fd = exportDmaBuffer(buf.index, nplane);
+		if (fd < 0)
+			goto out;
+
+		FrameBuffer::Plane plane;
+		plane.fd = fd;
+		plane.length = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ?
+			buf.m.planes[nplane].length : buf.length;
+
+		planes.emplace_back(plane);
+	}
+
+	if (!planes.empty())
+		frame = new FrameBuffer(planes);
+	else
+		LOG(V4L2, Error) << "Failed to get planes";
+out:
+	for (FrameBuffer::Plane &plane : planes)
+		::close(plane.fd);
+
+	return frame;
+}
+
 int V4L2VideoDevice::exportDmaBuffer(unsigned int index, unsigned int plane)
 {
 	struct v4l2_exportbuffer expbuf = {};
@@ -1062,6 +1157,35 @@ int V4L2VideoDevice::exportDmaBuffer(unsigned int index, unsigned int plane)
 	return expbuf.fd;
 }
 
+/**
+ * \brief Operate using buffers imported from external source
+ * \param[in] count Number of buffers to prepare for
+ * \return 0 on success or a negative error code otherwise
+ */
+int V4L2VideoDevice::externalBuffers(unsigned int count)
+{
+	/*
+	* We can only prepare the device to use external buffers when no
+	* internal buffers have been allocated.
+	*/
+	if (cache_)
+		return 0;
+
+	int ret;
+
+	memoryType_ = V4L2_MEMORY_DMABUF;
+
+	ret = requestBuffers(count);
+	if (ret)
+		return ret;
+
+	cache_ = new V4L2BufferCache(count);
+
+	LOG(V4L2, Debug) << "provided for " << count << " external buffers";
+
+	return 0;
+}
+
 /**
  * \brief Release all internally allocated buffers
  */
