libcamera: v4l2_device: Move start of frame detection to V4L2Device

The V4L2_EVENT_FRAME_SYNC event may occur on both V4L2 video-devices
(V4L2VideoDevice) and sub-devices (V4L2Subdevice). Move the start of
frame detection to the common base class of the two, V4L2Device.

There is no functional change.

Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
Niklas Söderlund 2020-10-18 03:47:23 +02:00
parent 68d2c41835
commit 3d624b745b
4 changed files with 87 additions and 84 deletions

View file

@ -13,11 +13,15 @@
#include <linux/videodev2.h> #include <linux/videodev2.h>
#include <libcamera/signal.h>
#include "libcamera/internal/log.h" #include "libcamera/internal/log.h"
#include "libcamera/internal/v4l2_controls.h" #include "libcamera/internal/v4l2_controls.h"
namespace libcamera { namespace libcamera {
class EventNotifier;
class V4L2Device : protected Loggable class V4L2Device : protected Loggable
{ {
public: public:
@ -34,6 +38,9 @@ public:
const std::string &deviceNode() const { return deviceNode_; } const std::string &deviceNode() const { return deviceNode_; }
std::string devicePath() const; std::string devicePath() const;
int setFrameStartEnabled(bool enable);
Signal<uint32_t> frameStart;
protected: protected:
V4L2Device(const std::string &deviceNode); V4L2Device(const std::string &deviceNode);
~V4L2Device(); ~V4L2Device();
@ -51,11 +58,16 @@ private:
const struct v4l2_ext_control *v4l2Ctrls, const struct v4l2_ext_control *v4l2Ctrls,
unsigned int count); unsigned int count);
void eventAvailable(EventNotifier *notifier);
std::map<unsigned int, struct v4l2_query_ext_ctrl> controlInfo_; std::map<unsigned int, struct v4l2_query_ext_ctrl> controlInfo_;
std::vector<std::unique_ptr<V4L2ControlId>> controlIds_; std::vector<std::unique_ptr<V4L2ControlId>> controlIds_;
ControlInfoMap controls_; ControlInfoMap controls_;
std::string deviceNode_; std::string deviceNode_;
int fd_; int fd_;
EventNotifier *fdEventNotifier_;
bool frameStartEnabled_;
}; };
} /* namespace libcamera */ } /* namespace libcamera */

View file

@ -207,9 +207,6 @@ public:
int queueBuffer(FrameBuffer *buffer); int queueBuffer(FrameBuffer *buffer);
Signal<FrameBuffer *> bufferReady; Signal<FrameBuffer *> bufferReady;
int setFrameStartEnabled(bool enable);
Signal<uint32_t> frameStart;
int streamOn(); int streamOn();
int streamOff(); int streamOff();
@ -243,8 +240,6 @@ private:
void bufferAvailable(EventNotifier *notifier); void bufferAvailable(EventNotifier *notifier);
FrameBuffer *dequeueBuffer(); FrameBuffer *dequeueBuffer();
void eventAvailable(EventNotifier *notifier);
V4L2Capability caps_; V4L2Capability caps_;
enum v4l2_buf_type bufferType_; enum v4l2_buf_type bufferType_;
@ -254,9 +249,6 @@ private:
std::map<unsigned int, FrameBuffer *> queuedBuffers_; std::map<unsigned int, FrameBuffer *> queuedBuffers_;
EventNotifier *fdBufferNotifier_; EventNotifier *fdBufferNotifier_;
EventNotifier *fdEventNotifier_;
bool frameStartEnabled_;
}; };
class V4L2M2MDevice class V4L2M2MDevice

View file

@ -16,6 +16,8 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <unistd.h> #include <unistd.h>
#include <libcamera/event_notifier.h>
#include "libcamera/internal/log.h" #include "libcamera/internal/log.h"
#include "libcamera/internal/sysfs.h" #include "libcamera/internal/sysfs.h"
#include "libcamera/internal/utils.h" #include "libcamera/internal/utils.h"
@ -52,7 +54,8 @@ LOG_DEFINE_CATEGORY(V4L2)
* at open() time, and the \a logTag to prefix log messages with. * at open() time, and the \a logTag to prefix log messages with.
*/ */
V4L2Device::V4L2Device(const std::string &deviceNode) V4L2Device::V4L2Device(const std::string &deviceNode)
: deviceNode_(deviceNode), fd_(-1) : deviceNode_(deviceNode), fd_(-1), fdEventNotifier_(nullptr),
frameStartEnabled_(false)
{ {
} }
@ -87,7 +90,7 @@ int V4L2Device::open(unsigned int flags)
return ret; return ret;
} }
fd_ = ret; setFd(ret);
listControls(); listControls();
@ -117,6 +120,10 @@ int V4L2Device::setFd(int fd)
fd_ = fd; fd_ = fd;
fdEventNotifier_ = new EventNotifier(fd_, EventNotifier::Exception);
fdEventNotifier_->activated.connect(this, &V4L2Device::eventAvailable);
fdEventNotifier_->setEnabled(false);
return 0; return 0;
} }
@ -130,6 +137,8 @@ void V4L2Device::close()
if (!isOpen()) if (!isOpen())
return; return;
delete fdEventNotifier_;
if (::close(fd_) < 0) if (::close(fd_) < 0)
LOG(V4L2, Error) << "Failed to close V4L2 device: " LOG(V4L2, Error) << "Failed to close V4L2 device: "
<< strerror(errno); << strerror(errno);
@ -395,6 +404,40 @@ std::string V4L2Device::devicePath() const
return path; return path;
} }
/**
* \brief Enable or disable frame start event notification
* \param[in] enable True to enable frame start events, false to disable them
*
* This function enables or disables generation of frame start events. Once
* enabled, the events are signalled through the frameStart signal.
*
* \return 0 on success, a negative error code otherwise
*/
int V4L2Device::setFrameStartEnabled(bool enable)
{
if (frameStartEnabled_ == enable)
return 0;
struct v4l2_event_subscription event{};
event.type = V4L2_EVENT_FRAME_SYNC;
unsigned long request = enable ? VIDIOC_SUBSCRIBE_EVENT
: VIDIOC_UNSUBSCRIBE_EVENT;
int ret = ioctl(request, &event);
if (enable && ret)
return ret;
fdEventNotifier_->setEnabled(enable);
frameStartEnabled_ = enable;
return ret;
}
/**
* \var V4L2Device::frameStart
* \brief A Signal emitted when capture of a frame has started
*/
/** /**
* \brief Perform an IOCTL system call on the device node * \brief Perform an IOCTL system call on the device node
* \param[in] request The IOCTL request code * \param[in] request The IOCTL request code
@ -518,4 +561,33 @@ void V4L2Device::updateControls(ControlList *ctrls,
} }
} }
/**
* \brief Slot to handle V4L2 events from the V4L2 device
* \param[in] notifier The event notifier
*
* When this slot is called, a V4L2 event is available to be dequeued from the
* device.
*/
void V4L2Device::eventAvailable([[maybe_unused]] EventNotifier *notifier)
{
struct v4l2_event event{};
int ret = ioctl(VIDIOC_DQEVENT, &event);
if (ret < 0) {
LOG(V4L2, Error)
<< "Failed to dequeue event, disabling event notifier";
fdEventNotifier_->setEnabled(false);
return;
}
if (event.type != V4L2_EVENT_FRAME_SYNC) {
LOG(V4L2, Error)
<< "Spurious event (" << event.type
<< "), disabling event notifier";
fdEventNotifier_->setEnabled(false);
return;
}
frameStart.emit(event.u.frame_sync.frame_sequence);
}
} /* namespace libcamera */ } /* namespace libcamera */

View file

@ -481,8 +481,7 @@ const std::string V4L2DeviceFormat::toString() const
* \param[in] deviceNode The file-system path to the video device node * \param[in] deviceNode The file-system path to the video device node
*/ */
V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode) V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode)
: V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr), : V4L2Device(deviceNode), cache_(nullptr), fdBufferNotifier_(nullptr)
fdEventNotifier_(nullptr), frameStartEnabled_(false)
{ {
/* /*
* We default to an MMAP based CAPTURE video device, however this will * We default to an MMAP based CAPTURE video device, however this will
@ -575,10 +574,6 @@ int V4L2VideoDevice::open()
fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable); fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable);
fdBufferNotifier_->setEnabled(false); fdBufferNotifier_->setEnabled(false);
fdEventNotifier_ = new EventNotifier(fd(), EventNotifier::Exception);
fdEventNotifier_->activated.connect(this, &V4L2VideoDevice::eventAvailable);
fdEventNotifier_->setEnabled(false);
LOG(V4L2, Debug) LOG(V4L2, Debug)
<< "Opened device " << caps_.bus_info() << ": " << "Opened device " << caps_.bus_info() << ": "
<< caps_.driver() << ": " << caps_.card(); << caps_.driver() << ": " << caps_.card();
@ -668,10 +663,6 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)
fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable); fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable);
fdBufferNotifier_->setEnabled(false); fdBufferNotifier_->setEnabled(false);
fdEventNotifier_ = new EventNotifier(fd(), EventNotifier::Exception);
fdEventNotifier_->activated.connect(this, &V4L2VideoDevice::eventAvailable);
fdEventNotifier_->setEnabled(false);
LOG(V4L2, Debug) LOG(V4L2, Debug)
<< "Opened device " << caps_.bus_info() << ": " << "Opened device " << caps_.bus_info() << ": "
<< caps_.driver() << ": " << caps_.card(); << caps_.driver() << ": " << caps_.card();
@ -689,7 +680,6 @@ void V4L2VideoDevice::close()
releaseBuffers(); releaseBuffers();
delete fdBufferNotifier_; delete fdBufferNotifier_;
delete fdEventNotifier_;
V4L2Device::close(); V4L2Device::close();
} }
@ -1544,74 +1534,11 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()
return buffer; return buffer;
} }
/**
* \brief Slot to handle V4L2 events from the V4L2 video device
* \param[in] notifier The event notifier
*
* When this slot is called, a V4L2 event is available to be dequeued from the
* device.
*/
void V4L2VideoDevice::eventAvailable([[maybe_unused]] EventNotifier *notifier)
{
struct v4l2_event event{};
int ret = ioctl(VIDIOC_DQEVENT, &event);
if (ret < 0) {
LOG(V4L2, Error)
<< "Failed to dequeue event, disabling event notifier";
fdEventNotifier_->setEnabled(false);
return;
}
if (event.type != V4L2_EVENT_FRAME_SYNC) {
LOG(V4L2, Error)
<< "Spurious event (" << event.type
<< "), disabling event notifier";
fdEventNotifier_->setEnabled(false);
return;
}
frameStart.emit(event.u.frame_sync.frame_sequence);
}
/** /**
* \var V4L2VideoDevice::bufferReady * \var V4L2VideoDevice::bufferReady
* \brief A Signal emitted when a framebuffer completes * \brief A Signal emitted when a framebuffer completes
*/ */
/**
* \brief Enable or disable frame start event notification
* \param[in] enable True to enable frame start events, false to disable them
*
* This function enables or disables generation of frame start events. Once
* enabled, the events are signalled through the frameStart signal.
*
* \return 0 on success, a negative error code otherwise
*/
int V4L2VideoDevice::setFrameStartEnabled(bool enable)
{
if (frameStartEnabled_ == enable)
return 0;
struct v4l2_event_subscription event{};
event.type = V4L2_EVENT_FRAME_SYNC;
unsigned long request = enable ? VIDIOC_SUBSCRIBE_EVENT
: VIDIOC_UNSUBSCRIBE_EVENT;
int ret = ioctl(request, &event);
if (enable && ret)
return ret;
fdEventNotifier_->setEnabled(enable);
frameStartEnabled_ = enable;
return ret;
}
/**
* \var V4L2VideoDevice::frameStart
* \brief A Signal emitted when capture of a frame has started
*/
/** /**
* \brief Start the video stream * \brief Start the video stream
* \return 0 on success or a negative error code otherwise * \return 0 on success or a negative error code otherwise