[libcamera-devel,03/27] libcamera: v4l2_device: Request buffers from the device

Message ID 20190206060818.13907-4-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Capture frames throught requests
Related show

Commit Message

Laurent Pinchart Feb. 6, 2019, 6:07 a.m. UTC
From: Kieran Bingham <kieran.bingham@ideasonboard.com>

Provide an exportBuffers() function which allocates buffers with the MMAP
method, exports them using the dmabuf API and populates the given BufferPool.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
 src/libcamera/include/v4l2_device.h |  17 ++-
 src/libcamera/v4l2_device.cpp       | 158 +++++++++++++++++++++++++++-
 2 files changed, 173 insertions(+), 2 deletions(-)

Patch

diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
index 87cde10d2417..510b74f12d05 100644
--- a/src/libcamera/include/v4l2_device.h
+++ b/src/libcamera/include/v4l2_device.h
@@ -8,11 +8,16 @@ 
 #define __LIBCAMERA_V4L2_DEVICE_H__
 
 #include <string>
+#include <vector>
 
 #include <linux/videodev2.h>
 
 namespace libcamera {
 
+class Buffer;
+class BufferPool;
+class MediaEntity;
+
 struct V4L2Capability final : v4l2_capability {
 	const char *driver() const
 	{
@@ -67,7 +72,6 @@  public:
 	unsigned int planesCount;
 };
 
-class MediaEntity;
 class V4L2Device
 {
 public:
@@ -89,6 +93,9 @@  public:
 	int getFormat(V4L2DeviceFormat *format);
 	int setFormat(V4L2DeviceFormat *format);
 
+	int exportBuffers(unsigned int count, BufferPool *pool);
+	int releaseBuffers();
+
 private:
 	int getFormatSingleplane(V4L2DeviceFormat *format);
 	int setFormatSingleplane(V4L2DeviceFormat *format);
@@ -96,10 +103,18 @@  private:
 	int getFormatMultiplane(V4L2DeviceFormat *format);
 	int setFormatMultiplane(V4L2DeviceFormat *format);
 
+	int requestBuffers(unsigned int count);
+	int createPlane(Buffer *buffer, unsigned int plane,
+			unsigned int length);
+
 	std::string deviceNode_;
 	int fd_;
 	V4L2Capability caps_;
+
 	enum v4l2_buf_type bufferType_;
+	enum v4l2_memory memoryType_;
+
+	BufferPool *bufferPool_;
 };
 
 } /* namespace libcamera */
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index 1823457529f5..2d0a1cb6abbe 100644
--- a/src/libcamera/v4l2_device.cpp
+++ b/src/libcamera/v4l2_device.cpp
@@ -10,6 +10,9 @@ 
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
+#include <vector>
+
+#include <libcamera/buffer.h>
 
 #include "log.h"
 #include "media_object.h"
@@ -209,8 +212,14 @@  LOG_DEFINE_CATEGORY(V4L2)
  * \param deviceNode The file-system path to the video device node
  */
 V4L2Device::V4L2Device(const std::string &deviceNode)
-	: deviceNode_(deviceNode), fd_(-1)
+	: deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr)
 {
+	/*
+	 * We default to an MMAP based CAPTURE device, however this will be
+	 * updated based upon the device capabilities.
+	 */
+	bufferType_ = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+	memoryType_ = V4L2_MEMORY_MMAP;
 }
 
 /**
@@ -305,6 +314,8 @@  void V4L2Device::close()
 	if (fd_ < 0)
 		return;
 
+	releaseBuffers();
+
 	::close(fd_);
 	fd_ = -1;
 }
@@ -475,4 +486,149 @@  int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
 	return 0;
 }
 
+int V4L2Device::requestBuffers(unsigned int count)
+{
+	struct v4l2_requestbuffers rb = {};
+	int ret;
+
+	rb.count = count;
+	rb.type = bufferType_;
+	rb.memory = memoryType_;
+
+	ret = ioctl(fd_, VIDIOC_REQBUFS, &rb);
+	if (ret < 0) {
+		ret = -errno;
+		LOG(V4L2, Error)
+			<< "Unable to request " << count << " buffers: "
+			<< strerror(-ret);
+		return ret;
+	}
+
+	LOG(V4L2, Debug)
+		<< deviceNode_ << ":" << rb.count << " buffers requested.";
+
+	return rb.count;
+}
+
+/**
+ * \brief Request \a count buffers to be allocated from the device and stored in
+ * the buffer pool provided.
+ * \param[in] count Number of buffers to allocate
+ * \param[out] pool BufferPool to populate with buffers
+ * \return 0 on success or a negative error code otherwise
+ */
+int V4L2Device::exportBuffers(unsigned int count, BufferPool *pool)
+{
+	unsigned int allocatedBuffers;
+	unsigned int i;
+	int ret;
+
+	memoryType_ = V4L2_MEMORY_MMAP;
+
+	ret = requestBuffers(count);
+	if (ret < 0)
+		return ret;
+
+	allocatedBuffers = ret;
+	if (allocatedBuffers < count) {
+		LOG(V4L2, Error) << "Not enough buffers provided by V4L2Device";
+		requestBuffers(0);
+		return -ENOMEM;
+	}
+
+	count = ret;
+
+	/* Map the buffers. */
+	for (i = 0; i < count; ++i) {
+		struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
+		struct v4l2_buffer buf = {};
+		struct Buffer &buffer = pool->buffers()[i];
+
+		buf.index = i;
+		buf.type = bufferType_;
+		buf.memory = memoryType_;
+		buf.length = VIDEO_MAX_PLANES;
+		buf.m.planes = planes;
+
+		ret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);
+		if (ret < 0) {
+			ret = -errno;
+			LOG(V4L2, Error)
+				<< "Unable to query buffer " << i << ": "
+				<< strerror(-ret);
+			break;
+		}
+
+		if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
+			for (unsigned int p = 0; p < buf.length; ++p) {
+				ret = createPlane(&buffer, p,
+						  buf.m.planes[p].length);
+				if (ret)
+					break;
+			}
+		} else {
+			ret = createPlane(&buffer, 0, buf.length);
+		}
+
+		if (ret) {
+			LOG(V4L2, Error) << "Failed to create plane";
+			break;
+		}
+	}
+
+	if (ret) {
+		requestBuffers(0);
+		pool->destroyBuffers();
+		return ret;
+	}
+
+	bufferPool_ = pool;
+
+	return 0;
+}
+
+int V4L2Device::createPlane(Buffer *buffer, unsigned int planeIndex,
+			    unsigned int length)
+{
+	struct v4l2_exportbuffer expbuf = {};
+	int ret;
+
+	LOG(V4L2, Debug)
+		<< "Buffer " << buffer->index()
+		<< " plane " << planeIndex
+		<< ": length=" << length;
+
+	expbuf.type = bufferType_;
+	expbuf.index = buffer->index();
+	expbuf.plane = planeIndex;
+	expbuf.flags = O_RDWR;
+
+	ret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);
+	if (ret < 0) {
+		ret = -errno;
+		LOG(V4L2, Error)
+			<< "Failed to export buffer: " << strerror(-ret);
+		return ret;
+	}
+
+	buffer->planes().emplace_back();
+	Plane &plane = buffer->planes().back();
+	plane.setDmabuf(expbuf.fd, length);
+
+	return 0;
+}
+
+/**
+ * \brief Release all internally allocated buffers
+ */
+int V4L2Device::releaseBuffers()
+{
+	LOG(V4L2, Debug) << "Releasing bufferPool";
+
+	requestBuffers(0);
+	bufferPool_ = nullptr;
+
+	return 0;
+}
+
 } /* namespace libcamera */