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>
This commit is contained in:
Kieran Bingham 2019-01-24 17:35:21 +00:00 committed by Laurent Pinchart
parent 93ec63b867
commit 771befc6dc
2 changed files with 173 additions and 2 deletions

View file

@ -8,11 +8,16 @@
#define __LIBCAMERA_V4L2_DEVICE_H__ #define __LIBCAMERA_V4L2_DEVICE_H__
#include <string> #include <string>
#include <vector>
#include <linux/videodev2.h> #include <linux/videodev2.h>
namespace libcamera { namespace libcamera {
class Buffer;
class BufferPool;
class MediaEntity;
struct V4L2Capability final : v4l2_capability { struct V4L2Capability final : v4l2_capability {
const char *driver() const const char *driver() const
{ {
@ -67,7 +72,6 @@ public:
unsigned int planesCount; unsigned int planesCount;
}; };
class MediaEntity;
class V4L2Device class V4L2Device
{ {
public: public:
@ -89,6 +93,9 @@ public:
int getFormat(V4L2DeviceFormat *format); int getFormat(V4L2DeviceFormat *format);
int setFormat(V4L2DeviceFormat *format); int setFormat(V4L2DeviceFormat *format);
int exportBuffers(unsigned int count, BufferPool *pool);
int releaseBuffers();
private: private:
int getFormatSingleplane(V4L2DeviceFormat *format); int getFormatSingleplane(V4L2DeviceFormat *format);
int setFormatSingleplane(V4L2DeviceFormat *format); int setFormatSingleplane(V4L2DeviceFormat *format);
@ -96,10 +103,18 @@ private:
int getFormatMultiplane(V4L2DeviceFormat *format); int getFormatMultiplane(V4L2DeviceFormat *format);
int setFormatMultiplane(V4L2DeviceFormat *format); int setFormatMultiplane(V4L2DeviceFormat *format);
int requestBuffers(unsigned int count);
int createPlane(Buffer *buffer, unsigned int plane,
unsigned int length);
std::string deviceNode_; std::string deviceNode_;
int fd_; int fd_;
V4L2Capability caps_; V4L2Capability caps_;
enum v4l2_buf_type bufferType_; enum v4l2_buf_type bufferType_;
enum v4l2_memory memoryType_;
BufferPool *bufferPool_;
}; };
} /* namespace libcamera */ } /* namespace libcamera */

View file

@ -10,6 +10,9 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h> #include <unistd.h>
#include <vector>
#include <libcamera/buffer.h>
#include "log.h" #include "log.h"
#include "media_object.h" #include "media_object.h"
@ -209,8 +212,14 @@ LOG_DEFINE_CATEGORY(V4L2)
* \param deviceNode The file-system path to the video device node * \param deviceNode The file-system path to the video device node
*/ */
V4L2Device::V4L2Device(const std::string &deviceNode) 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) if (fd_ < 0)
return; return;
releaseBuffers();
::close(fd_); ::close(fd_);
fd_ = -1; fd_ = -1;
} }
@ -475,4 +486,149 @@ int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
return 0; 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 */ } /* namespace libcamera */