diff options
Diffstat (limited to 'src/libcamera')
-rw-r--r-- | src/libcamera/include/v4l2_device.h | 17 | ||||
-rw-r--r-- | src/libcamera/v4l2_device.cpp | 158 |
2 files changed, 173 insertions, 2 deletions
diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h index 87cde10d..510b74f1 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 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 */ |