mirror of
https://git.libcamera.org/libcamera/libcamera.git
synced 2025-07-13 23:39:44 +03:00
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:
parent
93ec63b867
commit
771befc6dc
2 changed files with 173 additions and 2 deletions
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue