[libcamera-devel] [PATCH 2/2] libcamera: v4l2_videodevice: Add support for frame start events

Jacopo Mondi jacopo at jmondi.org
Sun Apr 26 21:40:02 CEST 2020


Hi Laurent,

On Sun, Apr 26, 2020 at 08:37:32PM +0300, Laurent Pinchart wrote:
> Hi Jacopo,
>
> On Sun, Apr 26, 2020 at 03:25:22PM +0200, Jacopo Mondi wrote:
> > On Sun, Apr 26, 2020 at 03:43:09AM +0300, Laurent Pinchart wrote:
> > > Extend the V4L2VideoDevice to support notifying of frame start events.
> > > The events are received from the device through the V4L2 event API, and
> > > passed to users of the class through a signal.
> > >
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > > ---
> > >  src/libcamera/include/v4l2_videodevice.h |  8 +++
> > >  src/libcamera/v4l2_videodevice.cpp       | 75 +++++++++++++++++++++++-
> > >  2 files changed, 82 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/src/libcamera/include/v4l2_videodevice.h b/src/libcamera/include/v4l2_videodevice.h
> > > index ee63d28da734..a0409e59c08f 100644
> > > --- a/src/libcamera/include/v4l2_videodevice.h
> > > +++ b/src/libcamera/include/v4l2_videodevice.h
> > > @@ -224,6 +224,9 @@ public:
> > >  	int queueBuffer(FrameBuffer *buffer);
> > >  	Signal<FrameBuffer *> bufferReady;
> > >
> > > +	int setFrameStartEnabled(bool enable);
> > > +	Signal<uint32_t> frameStart;
> > > +
> > >  	int streamOn();
> > >  	int streamOff();
> > >
> > > @@ -262,6 +265,8 @@ private:
> > >  	void bufferAvailable(EventNotifier *notifier);
> > >  	FrameBuffer *dequeueBuffer();
> > >
> > > +	void eventAvailable(EventNotifier *notifier);
> > > +
> > >  	V4L2Capability caps_;
> > >
> > >  	enum v4l2_buf_type bufferType_;
> > > @@ -271,6 +276,9 @@ private:
> > >  	std::map<unsigned int, FrameBuffer *> queuedBuffers_;
> > >
> > >  	EventNotifier *fdBufferNotifier_;
> > > +	EventNotifier *fdEventNotifier_;
> > > +
> > > +	bool frameStartEnabled_;
> > >  };
> > >
> > >  class V4L2M2MDevice
> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp
> > > index 18a71e4f8a7f..45aedb1a9490 100644
> > > --- a/src/libcamera/v4l2_videodevice.cpp
> > > +++ b/src/libcamera/v4l2_videodevice.cpp
> > > @@ -544,7 +544,8 @@ const std::string V4L2DeviceFormat::toString() const
> > >   * \param[in] deviceNode The file-system path to the video device node
> > >   */
> > >  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
> > > @@ -637,6 +638,10 @@ int V4L2VideoDevice::open()
> > >  	fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable);
> > >  	fdBufferNotifier_->setEnabled(false);
> > >
> > > +	fdEventNotifier_ = new EventNotifier(fd(), EventNotifier::Exception);
> > > +	fdEventNotifier_->activated.connect(this, &V4L2VideoDevice::eventAvailable);
> > > +	fdEventNotifier_->setEnabled(false);
> > > +
> > >  	LOG(V4L2, Debug)
> > >  		<< "Opened device " << caps_.bus_info() << ": "
> > >  		<< caps_.driver() << ": " << caps_.card();
> > > @@ -726,6 +731,10 @@ int V4L2VideoDevice::open(int handle, enum v4l2_buf_type type)
> > >  	fdBufferNotifier_->activated.connect(this, &V4L2VideoDevice::bufferAvailable);
> > >  	fdBufferNotifier_->setEnabled(false);
> > >
> > > +	fdEventNotifier_ = new EventNotifier(fd(), EventNotifier::Exception);
> > > +	fdEventNotifier_->activated.connect(this, &V4L2VideoDevice::eventAvailable);
> > > +	fdEventNotifier_->setEnabled(false);
> > > +
> > >  	LOG(V4L2, Debug)
> > >  		<< "Opened device " << caps_.bus_info() << ": "
> > >  		<< caps_.driver() << ": " << caps_.card();
> > > @@ -743,6 +752,7 @@ void V4L2VideoDevice::close()
> > >
> > >  	releaseBuffers();
> > >  	delete fdBufferNotifier_;
> > > +	delete fdEventNotifier_;
> > >
> > >  	V4L2Device::close();
> > >  }
> > > @@ -1570,11 +1580,74 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer()
> > >  	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(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);
> >
> > Should we stop emitting events if a spurious one is received ?
>
> This should really never happen, and if it does, there's nothing that
> guarantees we'll only get one spurious event. For all we know there
> could be a flood of them. I'd rather treat this as a hard error for that
> reason, until we realize there's a use case to still continue, and then
> decide what the best recovery process is.
>

Ack

> > > +		return;
> > > +	}
> > > +
> > > +	frameStart.emit(event.u.frame_sync.frame_sequence);
> > > +}
> > > +
> > >  /**
> > >   * \var V4L2VideoDevice::bufferReady
> > >   * \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
> >
> > to disable 'it' ?
>
> "them" refers to "events", which is a plural.
>

Oh! the final 'm' in them was under the column brake bar in my editor,
I though the statement was left un-complete :)

> > > + *
> > > + * 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;
> >
> > There's a very tiny window here that could make us miss a signal.
> > Enabling the notifier before instructing the video device to report
> > events would solve it. It will need handling the error path a bit more
> > carefully though.
>
> All the V4L2VideoDevice functions have to be called from the thread in
> which the V4L2VideoDevice object lives, so this won't be a problem in
> practice. The V4L2 fd will not be polled before control returns to the
> event loop of the thread, which is after this function returns.
>
> There could be use cases for relaxing this constraint, but we should
> then also consider queueuBuffer() as a candidate for thread-safety (and
> possibly other functions), and solve it globally for V4L2VideoDevice.
>

Ack

> > With this considered
> > Reviewed-by: Jacopo Mondi <jacopo at jmondi.org>
> >

Thanks for the explanation

> > > +
> > > +	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
> > >   * \return 0 on success or a negative error code otherwise
>
> --
> Regards,
>
> Laurent Pinchart


More information about the libcamera-devel mailing list