libcamera: v4l2_videodevice: Support M2M devices
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@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
parent
3a278eb460
commit
4f7625cca7
2 changed files with 219 additions and 1 deletions
|
@ -71,6 +71,11 @@ struct V4L2Capability final : v4l2_capability {
|
||||||
V4L2_CAP_VIDEO_OUTPUT |
|
V4L2_CAP_VIDEO_OUTPUT |
|
||||||
V4L2_CAP_VIDEO_OUTPUT_MPLANE);
|
V4L2_CAP_VIDEO_OUTPUT_MPLANE);
|
||||||
}
|
}
|
||||||
|
bool isM2M() const
|
||||||
|
{
|
||||||
|
return device_caps() & (V4L2_CAP_VIDEO_M2M |
|
||||||
|
V4L2_CAP_VIDEO_M2M_MPLANE);
|
||||||
|
}
|
||||||
bool isMeta() const
|
bool isMeta() const
|
||||||
{
|
{
|
||||||
return device_caps() & (V4L2_CAP_META_CAPTURE |
|
return device_caps() & (V4L2_CAP_META_CAPTURE |
|
||||||
|
@ -124,6 +129,7 @@ public:
|
||||||
V4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;
|
V4L2VideoDevice &operator=(const V4L2VideoDevice &) = delete;
|
||||||
|
|
||||||
int open();
|
int open();
|
||||||
|
int open(int handle, enum v4l2_buf_type type);
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
const char *driverName() const { return caps_.driver(); }
|
const char *driverName() const { return caps_.driver(); }
|
||||||
|
@ -182,6 +188,25 @@ private:
|
||||||
EventNotifier *fdEvent_;
|
EventNotifier *fdEvent_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class V4L2M2MDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
V4L2M2MDevice(const std::string &deviceNode);
|
||||||
|
~V4L2M2MDevice();
|
||||||
|
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
|
||||||
|
V4L2VideoDevice *output() { return output_; }
|
||||||
|
V4L2VideoDevice *capture() { return capture_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string deviceNode_;
|
||||||
|
|
||||||
|
V4L2VideoDevice *output_;
|
||||||
|
V4L2VideoDevice *capture_;
|
||||||
|
};
|
||||||
|
|
||||||
} /* namespace libcamera */
|
} /* namespace libcamera */
|
||||||
|
|
||||||
#endif /* __LIBCAMERA_V4L2_VIDEODEVICE_H__ */
|
#endif /* __LIBCAMERA_V4L2_VIDEODEVICE_H__ */
|
||||||
|
|
|
@ -89,6 +89,12 @@ LOG_DECLARE_CATEGORY(V4L2)
|
||||||
* \return True if the video device can capture or output images
|
* \return True if the video device can capture or output images
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn V4L2Capability::isM2M()
|
||||||
|
* \brief Identify if the device is a Memory-to-Memory device
|
||||||
|
* \return True if the device can capture and output images using the M2M API
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \fn V4L2Capability::isMeta()
|
* \fn V4L2Capability::isMeta()
|
||||||
* \brief Identify if the video device captures or outputs image meta-data
|
* \brief Identify if the video device captures or outputs image meta-data
|
||||||
|
@ -295,7 +301,8 @@ V4L2VideoDevice::~V4L2VideoDevice()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Open a V4L2 video device and query its capabilities
|
* \brief Open the V4L2 video device node and query its capabilities
|
||||||
|
*
|
||||||
* \return 0 on success or a negative error code otherwise
|
* \return 0 on success or a negative error code otherwise
|
||||||
*/
|
*/
|
||||||
int V4L2VideoDevice::open()
|
int V4L2VideoDevice::open()
|
||||||
|
@ -355,6 +362,92 @@ int V4L2VideoDevice::open()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Open a V4L2 video device from an opened file handle and query its
|
||||||
|
* capabilities
|
||||||
|
* \param[in] handle The file descriptor to set
|
||||||
|
* \param[in] type The device type to operate on
|
||||||
|
*
|
||||||
|
* This methods opens a video device from the existing file descriptor \a
|
||||||
|
* handle. Like open(), this method queries the capabilities of the device, but
|
||||||
|
* handles it according to the given device \a type instead of determining its
|
||||||
|
* type from the capabilities. This can be used to force a given device type for
|
||||||
|
* memory-to-memory devices.
|
||||||
|
*
|
||||||
|
* The file descriptor \a handle is duplicated, and the caller is responsible
|
||||||
|
* for closing the \a handle when it has no further use for it. The close()
|
||||||
|
* method will close the duplicated file descriptor, leaving \a handle
|
||||||
|
* untouched.
|
||||||
|
*
|
||||||
|
* \return 0 on success or a negative error code otherwise
|
||||||
|
*/
|
||||||
|
int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int newFd;
|
||||||
|
|
||||||
|
newFd = dup(handle);
|
||||||
|
if (newFd < 0) {
|
||||||
|
ret = -errno;
|
||||||
|
LOG(V4L2, Error) << "Failed to duplicate file handle: "
|
||||||
|
<< strerror(-ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = V4L2Device::setFd(newFd);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG(V4L2, Error) << "Failed to set file handle: "
|
||||||
|
<< strerror(-ret);
|
||||||
|
::close(newFd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ioctl(VIDIOC_QUERYCAP, &caps_);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG(V4L2, Error)
|
||||||
|
<< "Failed to query device capabilities: "
|
||||||
|
<< strerror(-ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!caps_.hasStreaming()) {
|
||||||
|
LOG(V4L2, Error) << "Device does not support streaming I/O";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set buffer type and wait for read notifications on CAPTURE video
|
||||||
|
* devices (POLLIN), and write notifications for OUTPUT video devices
|
||||||
|
* (POLLOUT).
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
fdEvent_->activated.connect(this, &V4L2VideoDevice::bufferAvailable);
|
||||||
|
fdEvent_->setEnabled(false);
|
||||||
|
|
||||||
|
LOG(V4L2, Debug)
|
||||||
|
<< "Opened device " << caps_.bus_info() << ": "
|
||||||
|
<< caps_.driver() << ": " << caps_.card();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Close the video device, releasing any resources acquired by open()
|
* \brief Close the video device, releasing any resources acquired by open()
|
||||||
*/
|
*/
|
||||||
|
@ -1143,4 +1236,104 @@ V4L2VideoDevice *V4L2VideoDevice::fromEntityName(const MediaDevice *media,
|
||||||
return new V4L2VideoDevice(mediaEntity);
|
return new V4L2VideoDevice(mediaEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class V4L2M2MDevice
|
||||||
|
* \brief Memory-to-Memory video device
|
||||||
|
*
|
||||||
|
* The V4L2M2MDevice manages two V4L2VideoDevice instances on the same
|
||||||
|
* deviceNode which operate together using two queues to implement the V4L2
|
||||||
|
* Memory to Memory API.
|
||||||
|
*
|
||||||
|
* The two devices should be opened by calling open() on the V4L2M2MDevice, and
|
||||||
|
* can be closed by calling close on the V4L2M2MDevice.
|
||||||
|
*
|
||||||
|
* Calling V4L2VideoDevice::open() and V4L2VideoDevice::close() on the capture
|
||||||
|
* or output V4L2VideoDevice is not permitted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn V4L2M2MDevice::output
|
||||||
|
* \brief Retrieve the output V4L2VideoDevice instance
|
||||||
|
* \return The output V4L2VideoDevice instance
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn V4L2M2MDevice::capture
|
||||||
|
* \brief Retrieve the capture V4L2VideoDevice instance
|
||||||
|
* \return The capture V4L2VideoDevice instance
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Create a new V4L2M2MDevice from the \a deviceNode
|
||||||
|
* \param[in] deviceNode The file-system path to the video device node
|
||||||
|
*/
|
||||||
|
V4L2M2MDevice::V4L2M2MDevice(const std::string &deviceNode)
|
||||||
|
: deviceNode_(deviceNode)
|
||||||
|
{
|
||||||
|
output_ = new V4L2VideoDevice(deviceNode);
|
||||||
|
capture_ = new V4L2VideoDevice(deviceNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
V4L2M2MDevice::~V4L2M2MDevice()
|
||||||
|
{
|
||||||
|
delete capture_;
|
||||||
|
delete output_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Open a V4L2 Memory to Memory device
|
||||||
|
*
|
||||||
|
* Open the device node and prepare the two V4L2VideoDevice instances to handle
|
||||||
|
* their respective buffer queues.
|
||||||
|
*
|
||||||
|
* \return 0 on success or a negative error code otherwise
|
||||||
|
*/
|
||||||
|
int V4L2M2MDevice::open()
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The output and capture V4L2VideoDevice instances use the same file
|
||||||
|
* handle for the same device node. The local file handle can be closed
|
||||||
|
* as the V4L2VideoDevice::open() retains a handle by duplicating the
|
||||||
|
* fd passed in.
|
||||||
|
*/
|
||||||
|
fd = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);
|
||||||
|
if (fd < 0) {
|
||||||
|
ret = -errno;
|
||||||
|
LOG(V4L2, Error)
|
||||||
|
<< "Failed to open V4L2 M2M device: " << strerror(-ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = output_->open(fd, V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ret = capture_->open(fd, V4L2_BUF_TYPE_VIDEO_CAPTURE);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
::close(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
close();
|
||||||
|
::close(fd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Close the memory-to-memory device, releasing any resources acquired by
|
||||||
|
* open()
|
||||||
|
*/
|
||||||
|
void V4L2M2MDevice::close()
|
||||||
|
{
|
||||||
|
capture_->close();
|
||||||
|
output_->close();
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace libcamera */
|
} /* namespace libcamera */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue