[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