[libcamera-devel] [PATCH 3/6] libcamera: v4l2_videodevice: Support M2M devices
Kieran Bingham
kieran.bingham at ideasonboard.com
Thu Aug 8 17:12:18 CEST 2019
V4L2 M2M devices represent a V4L2Device with two queues. One output, and
one capture on a single device node.
Represent this by instantiating a V4L2VideoDevice for each queue type,
and preparing each device for its queue.
Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
---
src/libcamera/include/v4l2_videodevice.h | 26 ++-
src/libcamera/v4l2_videodevice.cpp | 200 ++++++++++++++++++++---
2 files changed, 201 insertions(+), 25 deletions(-)
diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
index f5c8da93fcb5..24c58d787fde 100644
--- a/src/libcamera/include/v4l2_videodevice.h
+++ b/src/libcamera/include/v4l2_videodevice.h
@@ -71,6 +71,11 @@ struct V4L2Capability final : v4l2_capability {
V4L2_CAP_VIDEO_OUTPUT |
V4L2_CAP_VIDEO_OUTPUT_MPLANE);
}
+ bool isM2M() const
+ {
+ return device_caps() & (V4L2_CAP_VIDEO_M2M |
+ V4L2_CAP_VIDEO_M2M_MPLANE);
+ }
bool isMeta() const
{
return device_caps() & (V4L2_CAP_META_CAPTURE |
@@ -123,7 +128,7 @@ public:
V4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;
- int open();
+ int open(int fd = -1, enum v4l2_buf_type type = V4L2_BUF_TYPE_PRIVATE);
void close();
const char *driverName() const { return caps_.driver(); }
@@ -152,6 +157,9 @@ protected:
std::string logPrefix() const;
private:
+ int queryBufferType();
+ int queryBufferType(enum v4l2_buf_type type);
+
int getFormatMeta(V4L2DeviceFormat *format);
int setFormatMeta(V4L2DeviceFormat *format);
@@ -182,6 +190,22 @@ private:
EventNotifier *fdEvent_;
};
+class V4L2M2MDevice
+{
+public:
+ V4L2M2MDevice(const std::string &deviceNode);
+ ~V4L2M2MDevice();
+
+ V4L2VideoDevice *output() { return output_; }
+ V4L2VideoDevice *capture() { return capture_; }
+ unsigned int status() { return status_; }
+
+private:
+ V4L2VideoDevice *output_;
+ V4L2VideoDevice *capture_;
+ unsigned int status_;
+};
+
} /* namespace libcamera */
#endif /* __LIBCAMERA_V4L2_VIDEODEVICE_H__ */
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
index 81098dd70190..4bd33af5f3de 100644
--- a/src/libcamera/v4l2_videodevice.cpp
+++ b/src/libcamera/v4l2_videodevice.cpp
@@ -89,6 +89,12 @@ LOG_DECLARE_CATEGORY(V4L2)
* \return True if the video device can capture or output images
*/
+/**
+ * \fn V4L2Capability::isM2M()
+ * \brief Identify if the device is an M2M device
+ * \return True if the device can capture and output images using the M2M API
+ */
+
/**
* \fn V4L2Capability::isMeta()
* \brief Identify if the video device captures or outputs image meta-data
@@ -294,17 +300,80 @@ V4L2VideoDevice::~V4L2VideoDevice()
close();
}
+int V4L2VideoDevice::queryBufferType()
+{
+ if (caps_.isVideoCapture()) {
+ fdEvent_ = new EventNotifier(fd(), EventNotifier::Read);
+ bufferType_ = caps_.isMultiplanar()
+ ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
+ : V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ } else if (caps_.isVideoOutput()) {
+ fdEvent_ = new EventNotifier(fd(), EventNotifier::Write);
+ bufferType_ = caps_.isMultiplanar()
+ ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
+ : V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ } else if (caps_.isMetaCapture()) {
+ fdEvent_ = new EventNotifier(fd(), EventNotifier::Read);
+ bufferType_ = V4L2_BUF_TYPE_META_CAPTURE;
+ } else if (caps_.isMetaOutput()) {
+ fdEvent_ = new EventNotifier(fd(), EventNotifier::Write);
+ bufferType_ = V4L2_BUF_TYPE_META_OUTPUT;
+ } else {
+ LOG(V4L2, Error) << "Device is not a supported type";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int V4L2VideoDevice::queryBufferType(enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ fdEvent_ = new EventNotifier(fd(), EventNotifier::Write);
+ bufferType_ = caps_.isMultiplanar()
+ ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
+ : V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ fdEvent_ = new EventNotifier(fd(), EventNotifier::Read);
+ bufferType_ = caps_.isMultiplanar()
+ ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
+ : V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ default:
+ LOG(V4L2, Error) << "Unsupported buffer type";
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* \brief Open a V4L2 video device and query its capabilities
+ *
+ * \param[in] fd The file descriptor to set (optional)
+ * \param[in] type The device type to operate on (optional)
+ *
+ * Opens a device or sets the file descriptor if provided, and then queries the
+ * capabilities of the device and establishes the buffer types and device events
+ * accordingly.
+ *
* \return 0 on success or a negative error code otherwise
*/
-int V4L2VideoDevice::open()
+int V4L2VideoDevice::open(int fd, enum v4l2_buf_type type)
{
int ret;
- ret = V4L2Device::open(O_RDWR | O_NONBLOCK);
- if (ret < 0)
- return ret;
+ if (fd != -1) {
+ ret = V4L2Device::setFd(fd);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = V4L2Device::open(O_RDWR | O_NONBLOCK);
+ if (ret < 0)
+ return ret;
+ }
ret = ioctl(VIDIOC_QUERYCAP, &caps_);
if (ret < 0) {
@@ -324,26 +393,10 @@ int V4L2VideoDevice::open()
* devices (POLLIN), and write notifications for OUTPUT video devices
* (POLLOUT).
*/
- if (caps_.isVideoCapture()) {
- fdEvent_ = new EventNotifier(fd(), EventNotifier::Read);
- bufferType_ = caps_.isMultiplanar()
- ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
- : V4L2_BUF_TYPE_VIDEO_CAPTURE;
- } else if (caps_.isVideoOutput()) {
- fdEvent_ = new EventNotifier(fd(), EventNotifier::Write);
- bufferType_ = caps_.isMultiplanar()
- ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE
- : V4L2_BUF_TYPE_VIDEO_OUTPUT;
- } else if (caps_.isMetaCapture()) {
- fdEvent_ = new EventNotifier(fd(), EventNotifier::Read);
- bufferType_ = V4L2_BUF_TYPE_META_CAPTURE;
- } else if (caps_.isMetaOutput()) {
- fdEvent_ = new EventNotifier(fd(), EventNotifier::Write);
- bufferType_ = V4L2_BUF_TYPE_META_OUTPUT;
- } else {
- LOG(V4L2, Error) << "Device is not a supported type";
- return -EINVAL;
- }
+ if (type != V4L2_BUF_TYPE_PRIVATE)
+ queryBufferType(type);
+ else
+ queryBufferType();
fdEvent_->activated.connect(this, &V4L2VideoDevice::bufferAvailable);
fdEvent_->setEnabled(false);
@@ -1143,4 +1196,103 @@ V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media,
return new V4L2VideoDevice(mediaEntity);
}
+/**
+ * \class V4L2M2MDevice
+ * \brief Memory2Memory device container
+ *
+ * The V4L2M2MDevice manages two V4L2Device instances on the same
+ * deviceNode which operate together using two queues to implement the V4L2
+ * Memory to Memory API.
+ *
+ * V4L2M2MDevices are opened at the point they are created, and will return
+ * both a capture and an output device or neither in the event of failure.
+ *
+ * The two devices should be configured and started as normal V4L2Device
+ * instances. Closing either of the devices will require recreating a new
+ * V4L2M2MDevice.
+ *
+ * The two V4L2Device instances share a single device node which is opened at
+ * the point the V4L2M2MDevice is created.
+ */
+
+/**
+ * \fn V4L2M2MDevice::output
+ * \return The output V4L2Device instance
+ */
+
+/**
+ * \fn V4L2M2MDevice::capture
+ * \return The capture V4L2Device instance
+ */
+
+/**
+ * \fn V4L2M2MDevice::status
+ * \return The construction status of the V4L2M2MDevice
+ */
+
+/**
+ * \brief Create a new V4L2M2MDevice from the \a deviceNode
+ *
+ * The deviceNode will be opened and shared across two V4L2VideoDevice
+ * instances. One to control the output stream, and one to control the capture
+ * stream.
+ *
+ * After construction, the status() must be checked to validate the instance.
+ */
+V4L2M2MDevice::V4L2M2MDevice(const std::string &deviceNode)
+ : status_(0)
+{
+ int fd[2] = { -1, -1 };
+ int ret;
+
+ output_ = new V4L2VideoDevice(deviceNode);
+ capture_ = new V4L2VideoDevice(deviceNode);
+
+ /* Both V4L2Devices will have the same deviceNode. */
+ fd[0] = ::open(deviceNode.c_str(), O_RDWR | O_NONBLOCK);
+ if (fd[0] < 0) {
+ ret = -errno;
+ LOG(V4L2, Error)
+ << "Failed to open V4L2 M2M device: " << strerror(-ret);
+ goto err;
+ }
+
+ fd[1] = dup(fd[0]);
+ if (fd[1] < 0) {
+ ret = -errno;
+ LOG(V4L2, Error)
+ << "Failed to duplicate M2M device: " << strerror(-ret);
+
+ goto err;
+ }
+
+ ret = output_->open(fd[0], V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (ret)
+ goto err;
+
+ ret = capture_->open(fd[1], V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ if (ret)
+ goto err;
+
+ return;
+
+err:
+ delete capture_;
+ delete output_;
+
+ capture_ = nullptr;
+ output_ = nullptr;
+
+ status_ = ret;
+
+ close(fd[1]);
+ close(fd[0]);
+}
+
+V4L2M2MDevice::~V4L2M2MDevice()
+{
+ delete capture_;
+ delete output_;
+}
+
} /* namespace libcamera */
--
2.20.1
More information about the libcamera-devel
mailing list