[libcamera-devel] [PATCH 19/30] libcamera: v4l2_videodevice: Add new buffer interface

Niklas Söderlund niklas.soderlund at ragnatech.se
Wed Nov 27 00:36:09 CET 2019


Extend V4L2VideoDevice with two new functions, one to deal with
allocating buffers from the video device and one to prepare the video
device to use external buffers.

The two new functions intend to replace exportBuffers() and
importBuffers() once the transition to the FrameBuffer interface is
complete.

Signed-off-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
---
 src/libcamera/include/v4l2_videodevice.h |   4 +
 src/libcamera/v4l2_videodevice.cpp       | 126 ++++++++++++++++++++++-
 2 files changed, 129 insertions(+), 1 deletion(-)

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
  */
-- 
2.24.0



More information about the libcamera-devel mailing list