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

Message ID 20190203110102.5663-4-kieran.bingham@ideasonboard.com
State Accepted
Headers show
Series
  • libcamera: V4L2 Streams
Related show

Commit Message

Kieran Bingham Feb. 3, 2019, 11 a.m. UTC
Provide a requestBuffers() interface to allocate buffers from the V4L2Device
and keep a vector of the buffers in the V4L2Device object for later queue and
dequeue operations.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 include/libcamera/buffer.h          |   4 +-
 src/libcamera/include/v4l2_device.h |  10 +++
 src/libcamera/v4l2_device.cpp       | 111 +++++++++++++++++++++++++++-
 3 files changed, 122 insertions(+), 3 deletions(-)

Patch

diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
index e730fc0f84fd..97fab5c65cce 100644
--- a/include/libcamera/buffer.h
+++ b/include/libcamera/buffer.h
@@ -71,10 +71,10 @@  public:
 	void free();
 
 	unsigned int count() const { return buffers_.size(); };
-	const std::vector<Buffer *> &buffers() { return buffers_; };
+	std::vector<Buffer *> &buffers() { return buffers_; };
 
 private:
-	virtual int allocateMemory() = 0;
+	virtual int allocateMemory() { return 0; };
 
 	BufferPool::Memory memory_;
 	std::vector<Buffer *> buffers_;
diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
index 9e9eada94130..6599ce2d761c 100644
--- a/src/libcamera/include/v4l2_device.h
+++ b/src/libcamera/include/v4l2_device.h
@@ -8,6 +8,7 @@ 
 #define __LIBCAMERA_V4L2_DEVICE_H__
 
 #include <string>
+#include <vector>
 
 #include <linux/videodev2.h>
 
@@ -76,6 +77,7 @@  public:
 };
 
 class MediaEntity;
+
 class V4L2Device
 {
 public:
@@ -97,6 +99,8 @@  public:
 	int format(V4L2DeviceFormat *fmt);
 	int setFormat(V4L2DeviceFormat *fmt);
 
+	BufferPool *requestBuffers(unsigned int qty = 8);
+
 private:
 	int getFormatSingleplane(V4L2DeviceFormat *fmt);
 	int setFormatSingleplane(V4L2DeviceFormat *fmt);
@@ -104,10 +108,16 @@  private:
 	int getFormatMultiplane(V4L2DeviceFormat *fmt);
 	int setFormatMultiplane(V4L2DeviceFormat *fmt);
 
+	int requestBuffers(unsigned int qty, std::vector<Buffer *> &buffers);
+
 	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 d507ea9700b3..8bcd8bbc34f6 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"
@@ -187,8 +190,14 @@  V4L2Buffer::V4L2Buffer(struct v4l2_buffer &vb)
  * \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;
 }
 
 /**
@@ -205,6 +214,8 @@  V4L2Device::V4L2Device(const MediaEntity &entity)
 V4L2Device::~V4L2Device()
 {
 	close();
+
+	delete bufferPool_;
 }
 
 /**
@@ -305,6 +316,104 @@  void V4L2Device::close()
  * \return The string containing the device location
  */
 
+/**
+ * \brief Request \a qty buffers to be allocated from the device and returned
+ * as a BufferPool
+ * \return A BufferPool containing the device buffers, or nullptr if the
+ * allocation fails
+ */
+BufferPool *V4L2Device::requestBuffers(unsigned int qty)
+{
+	int ret;
+
+	if (bufferPool_) {
+		LOG(V4L2, Warning) << "Pre-existing buffer pool will be lost";
+		delete bufferPool_;
+	}
+
+	bufferPool_ = new BufferPool(BufferPool::Internal);
+
+	ret = requestBuffers(qty, bufferPool_->buffers());
+	if (ret) {
+		delete bufferPool_;
+		bufferPool_ = nullptr;
+		return nullptr;
+	}
+
+	return bufferPool_;
+}
+
+/**
+ * \brief Request \a qty buffers from the V4L2Device and track Buffer
+ * representations within the specified \a buffers target.
+ */
+int V4L2Device::requestBuffers(unsigned int qty, std::vector<Buffer *> &buffers)
+{
+	struct v4l2_requestbuffers rb = {};
+	unsigned int i;
+	int ret;
+
+	rb.count = qty;
+	rb.type = bufferType_;
+	rb.memory = memoryType_; /** \todo Support dmaBuf memoryTypes */
+
+	ret = ioctl(fd_, VIDIOC_REQBUFS, &rb);
+	if (ret < 0) {
+		ret = -errno;
+		LOG(V4L2, Error)
+			<< "Unable to request " << qty << " buffers: "
+			<< strerror(-ret);
+		return ret;
+	}
+
+	LOG(V4L2, Debug)
+		<< deviceNode_ << ":" << rb.count << " buffers requested.";
+
+	if (memoryType_ != V4L2_MEMORY_MMAP) {
+		LOG(V4L2, Error)
+			<< "Non-mmap allocations are not yet supported.";
+		return -EINVAL;
+	}
+
+	/* Map the buffers. */
+	for (i = 0; i < rb.count; ++i) {
+		struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
+		struct v4l2_buffer buf = {};
+		struct Buffer *buffer;
+
+		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) {
+			LOG(V4L2, Error)
+				<< "Unable to query buffer " << i << ": "
+				<< strerror(errno);
+			return ret;
+		}
+
+		if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
+			for (unsigned int p = 0; p < buf.length; ++p)
+				LOG(V4L2, Debug)
+					<< "plane: " << p
+					<< " length: " << buf.m.planes[p].length
+					<< " offset: "
+					<< buf.m.planes[p].data_offset;
+		} else {
+			LOG(V4L2, Debug) << "length: " << buf.length
+					 << " offset: " << buf.m.offset;
+		}
+
+		buffer = new V4L2Buffer(buf);
+		buffers.push_back(buffer);
+	}
+
+	return 0;
+}
+
 /**
  * \brief Retrieve the image format set on the V4L2 device
  * \return 0 for success, a negative error code otherwise