Message ID | 20220406123554.4042163-2-naush@raspberrypi.com |
---|---|
State | Accepted |
Commit | 16f3d2de50ffa35295240e247d8cc4cffabf1a53 |
Headers | show |
Series |
|
Related | show |
Hi Naush, On 4/6/22 18:05, Naushir Patuck via libcamera-devel wrote: > Add a timer that gets reset on every buffer dequeue event. If the timeout > expires, optionally call a slot in the pipeline handler to handle this > condition. This may be useful in detecting and handling stalls in either the > hardware or device driver. > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > --- > include/libcamera/internal/v4l2_videodevice.h | 10 ++++ > src/libcamera/v4l2_videodevice.cpp | 53 +++++++++++++++++++ > 2 files changed, 63 insertions(+) > > diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h > index cfeae7bd6c52..9c9493cc16ed 100644 > --- a/include/libcamera/internal/v4l2_videodevice.h > +++ b/include/libcamera/internal/v4l2_videodevice.h > @@ -20,7 +20,9 @@ > #include <libcamera/base/class.h> > #include <libcamera/base/log.h> > #include <libcamera/base/signal.h> > +#include <libcamera/base/timer.h> > #include <libcamera/base/unique_fd.h> > +#include <libcamera/base/utils.h> > > #include <libcamera/color_space.h> > #include <libcamera/framebuffer.h> > @@ -217,6 +219,9 @@ public: > int streamOn(); > int streamOff(); > > + void setDequeueTimeout(utils::Duration timeout); > + Signal<> dequeueTimeout; > + > static std::unique_ptr<V4L2VideoDevice> > fromEntityName(const MediaDevice *media, const std::string &entity); > > @@ -253,6 +258,8 @@ private: > void bufferAvailable(); > FrameBuffer *dequeueBuffer(); > > + void watchdogExpired(); > + > V4L2Capability caps_; > V4L2DeviceFormat format_; > const PixelFormatInfo *formatInfo_; > @@ -266,6 +273,9 @@ private: > EventNotifier *fdBufferNotifier_; > > State state_; > + > + Timer watchdog_; > + utils::Duration watchdogDuration_; This ended up being used un-initialised which in turn polluted the log: https://paste.debian.net/1236996/ I have posted a patch "libcamera: v4l2_videodevice: Fix uninitialised watchdogDuration_" to fix it > }; > > class V4L2M2MDevice > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp > index 0830be80c553..c3a1b6c42e19 100644 > --- a/src/libcamera/v4l2_videodevice.cpp > +++ b/src/libcamera/v4l2_videodevice.cpp > @@ -539,6 +539,7 @@ V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode) > V4L2VideoDevice::V4L2VideoDevice(const MediaEntity *entity) > : V4L2VideoDevice(entity->deviceNode()) > { > + watchdog_.timeout.connect(this, &V4L2VideoDevice::watchdogExpired); > } > > V4L2VideoDevice::~V4L2VideoDevice() > @@ -1726,6 +1727,9 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer() > return nullptr; > } > > + if (watchdogDuration_.count()) > + watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_)); > + > cache_->put(buf.index); > > FrameBuffer *buffer = it->second; > @@ -1828,6 +1832,8 @@ int V4L2VideoDevice::streamOn() > } > > state_ = State::Streaming; > + if (watchdogDuration_.count()) > + watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_)); > > return 0; > } > @@ -1852,6 +1858,9 @@ int V4L2VideoDevice::streamOff() > if (state_ != State::Streaming && queuedBuffers_.empty()) > return 0; > > + if (watchdogDuration_.count()) > + watchdog_.stop(); > + > ret = ioctl(VIDIOC_STREAMOFF, &bufferType_); > if (ret < 0) { > LOG(V4L2, Error) > @@ -1879,6 +1888,50 @@ int V4L2VideoDevice::streamOff() > return 0; > } > > +/** > + * \brief Set the dequeue timeout value > + * \param[in] timeout The timeout value to be used > + * > + * Sets a timeout value, given by \a timeout, that will be used by a watchdog > + * timer to ensure buffer dequeue events are periodically occurring when the > + * device is streaming. The watchdog timer is only active when the device is > + * streaming, so it is not necessary to disable it when the device stops > + * streaming. The timeout value can be safely updated at any time. > + * > + * If the timer expires, the \ref V4L2VideoDevice::dequeueTimeout signal is > + * emitted. This can typically be used by pipeline handlers to be notified of > + * stalled devices. > + * > + * Set \a timeout to 0 to disable the watchdog timer. > + */ > +void V4L2VideoDevice::setDequeueTimeout(utils::Duration timeout) > +{ > + watchdogDuration_ = timeout; > + > + watchdog_.stop(); > + if (watchdogDuration_.count() && state_ == State::Streaming) > + watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout)); > +} > + > +/** > + * \var V4L2VideoDevice::dequeueTimeout > + * \brief A Signal emitted when the dequeue watchdog timer expires > + */ > + > +/** > + * \brief Slot to handle an expired dequeue timer > + * > + * When this slot is called, the time between successive dequeue events is over > + * the required timeout. Emit the \ref V4L2VideoDevice::dequeueTimeout signal. > + */ > +void V4L2VideoDevice::watchdogExpired() > +{ > + LOG(V4L2, Warning) > + << "Dequeue timer of " << watchdogDuration_ << " has expired!"; > + > + dequeueTimeout.emit(); > +} > + > /** > * \brief Create a new video device instance from \a entity in media device > * \a media
diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h index cfeae7bd6c52..9c9493cc16ed 100644 --- a/include/libcamera/internal/v4l2_videodevice.h +++ b/include/libcamera/internal/v4l2_videodevice.h @@ -20,7 +20,9 @@ #include <libcamera/base/class.h> #include <libcamera/base/log.h> #include <libcamera/base/signal.h> +#include <libcamera/base/timer.h> #include <libcamera/base/unique_fd.h> +#include <libcamera/base/utils.h> #include <libcamera/color_space.h> #include <libcamera/framebuffer.h> @@ -217,6 +219,9 @@ public: int streamOn(); int streamOff(); + void setDequeueTimeout(utils::Duration timeout); + Signal<> dequeueTimeout; + static std::unique_ptr<V4L2VideoDevice> fromEntityName(const MediaDevice *media, const std::string &entity); @@ -253,6 +258,8 @@ private: void bufferAvailable(); FrameBuffer *dequeueBuffer(); + void watchdogExpired(); + V4L2Capability caps_; V4L2DeviceFormat format_; const PixelFormatInfo *formatInfo_; @@ -266,6 +273,9 @@ private: EventNotifier *fdBufferNotifier_; State state_; + + Timer watchdog_; + utils::Duration watchdogDuration_; }; class V4L2M2MDevice diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 0830be80c553..c3a1b6c42e19 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -539,6 +539,7 @@ V4L2VideoDevice::V4L2VideoDevice(const std::string &deviceNode) V4L2VideoDevice::V4L2VideoDevice(const MediaEntity *entity) : V4L2VideoDevice(entity->deviceNode()) { + watchdog_.timeout.connect(this, &V4L2VideoDevice::watchdogExpired); } V4L2VideoDevice::~V4L2VideoDevice() @@ -1726,6 +1727,9 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer() return nullptr; } + if (watchdogDuration_.count()) + watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_)); + cache_->put(buf.index); FrameBuffer *buffer = it->second; @@ -1828,6 +1832,8 @@ int V4L2VideoDevice::streamOn() } state_ = State::Streaming; + if (watchdogDuration_.count()) + watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_)); return 0; } @@ -1852,6 +1858,9 @@ int V4L2VideoDevice::streamOff() if (state_ != State::Streaming && queuedBuffers_.empty()) return 0; + if (watchdogDuration_.count()) + watchdog_.stop(); + ret = ioctl(VIDIOC_STREAMOFF, &bufferType_); if (ret < 0) { LOG(V4L2, Error) @@ -1879,6 +1888,50 @@ int V4L2VideoDevice::streamOff() return 0; } +/** + * \brief Set the dequeue timeout value + * \param[in] timeout The timeout value to be used + * + * Sets a timeout value, given by \a timeout, that will be used by a watchdog + * timer to ensure buffer dequeue events are periodically occurring when the + * device is streaming. The watchdog timer is only active when the device is + * streaming, so it is not necessary to disable it when the device stops + * streaming. The timeout value can be safely updated at any time. + * + * If the timer expires, the \ref V4L2VideoDevice::dequeueTimeout signal is + * emitted. This can typically be used by pipeline handlers to be notified of + * stalled devices. + * + * Set \a timeout to 0 to disable the watchdog timer. + */ +void V4L2VideoDevice::setDequeueTimeout(utils::Duration timeout) +{ + watchdogDuration_ = timeout; + + watchdog_.stop(); + if (watchdogDuration_.count() && state_ == State::Streaming) + watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(timeout)); +} + +/** + * \var V4L2VideoDevice::dequeueTimeout + * \brief A Signal emitted when the dequeue watchdog timer expires + */ + +/** + * \brief Slot to handle an expired dequeue timer + * + * When this slot is called, the time between successive dequeue events is over + * the required timeout. Emit the \ref V4L2VideoDevice::dequeueTimeout signal. + */ +void V4L2VideoDevice::watchdogExpired() +{ + LOG(V4L2, Warning) + << "Dequeue timer of " << watchdogDuration_ << " has expired!"; + + dequeueTimeout.emit(); +} + /** * \brief Create a new video device instance from \a entity in media device * \a media