summaryrefslogtreecommitdiff
path: root/src/libcamera/v4l2_device.cpp
diff options
context:
space:
mode:
authorKieran Bingham <kieran.bingham@ideasonboard.com>2019-01-24 17:35:21 +0000
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2019-02-06 06:49:29 +0200
commit771befc6dc0e55f4c94c949b54bba6821740aff7 (patch)
tree262fd0125c7f69c203008b6b8539cfece210611a /src/libcamera/v4l2_device.cpp
parent93ec63b8673b786065df815ab7035891cfc3a56c (diff)
libcamera: v4l2_device: Request buffers from the device
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>
Diffstat (limited to 'src/libcamera/v4l2_device.cpp')
-rw-r--r--src/libcamera/v4l2_device.cpp158
1 files changed, 157 insertions, 1 deletions
diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
index 18234575..2d0a1cb6 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 */