[libcamera-devel] [PATCH] libcamera: Introduce V4L2Device base class
Jacopo Mondi
jacopo at jmondi.org
Wed Jun 12 13:46:03 CEST 2019
Hi Kieran,
On Wed, Jun 12, 2019 at 12:21:21PM +0100, Kieran Bingham wrote:
> Hi Jacopo,
>
> On 12/06/2019 11:41, Jacopo Mondi wrote:
> > The V4L2 devices and subdevices share a few common operations and fields,
> > like opening and closing a device node, and perform IOCTLs on the
> > device's file descriptor. With the forthcoming introduction of support
> > for V4L2 controls, the quantity of shared code will drastically increase,
> > as the control implementation is identical for the two.
> >
> > To maximize code re-use and avoid duplications, provide a V4L2Device
> > base class which groups the common operations and members, and perform a
> > tree-wide rename of V4L2Device and V4L2Subdevice in V4L2Videodev and
> > V4L2Subdev respectively.
>
> Bikeshedding here, should it be V4L2VideoDev / V4L2SubDev ?
>
I have no strong opinion. I would say I'm fine with what I have here,
since renaming is painful, but if you feel strong on this, I can
change it.
> I think we commonly use subdev as a single word, but I don't think
> videodev gets the same treatment usually?
>
> > The newly introduced base class provides methods to open/close a device
> > node, access the file descriptor, and perform IOCTLs on the device.
> >
> > Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> > ---
> > This patch is big, and will get in the way of many of the currently in-review
> > series. At the same time, having a common base where to implement support for
> > V4L2 controls is a requirement, and so I bit the bullet and went on the huge
> > renaming operation you have here implemented.
> >
> > I renamed V4L2Device->V4L2Videodev and V4L2Subdevice->V4L2Subdev everywhere.
> > I intentionally left the V4L2DeviceFormat and V4L2SubdeviceFormat un-touched
> > as Niklas' series is going to remove them, so there was not point of making
> > things more complex than necessary here.
> >
> > I'm sending this one alone, separately from the V4L2 control suppor, to fast
> > track it and have it merged sooner than later. I know this will cause a lot
> > of work in-flight to be rebased, I'm sorry about it, but sooner or later this
> > had to be done.
> >
> > Thanks
> > j
>
> Checkstyle on this patch runs pretty clean, considering the amount of
> code it touches and some of the checkstyle warnings should certainly be
> ignored.
>
> These are the ones I'd fix up:
>
> --- src/libcamera/v4l2_device.cpp
> +++ src/libcamera/v4l2_device.cpp
> @@ -48,8 +48,8 @@
> *
>
> * Initialize the file descriptor to -1.
> */
> -V4L2Device::V4L2Device() :
> - fd_(-1)
> +V4L2Device::V4L2Device()
> + : fd_(-1)
> {
> }
>
> --- src/libcamera/v4l2_subdev.cpp
> +++ src/libcamera/v4l2_subdev.cpp
> @@ -302,7 +302,7 @@
> return "'" + entity_->name() + "'";
> }
>
> -int V4L2Subdev::enumPadSizes(unsigned int pad,unsigned int code,
> +int V4L2Subdev::enumPadSizes(unsigned int pad, unsigned int code,
> std::vector<SizeRange> *sizes)
> {
> struct v4l2_subdev_frame_size_enum sizeEnum = {};
>
> --- src/libcamera/v4l2_videodev.cpp
> +++ src/libcamera/v4l2_videodev.cpp
> @@ -712,7 +712,7 @@
> }
>
> int V4L2Videodev::createPlane(Buffer *buffer, unsigned int planeIndex,
> - unsigned int length)
> + unsigned int length)
> {
> struct v4l2_exportbuffer expbuf = {};
> int ret;
>
>
> Because this is just a global rename, There will be lots of other
> 'alignment issues' from this patch:
>
> Such as :
>
> > - viewfinder_.dev = V4L2Device::fromEntityName(media,
> > + viewfinder_.dev = V4L2Videodev::fromEntityName(media,
> > name_ + " viewfinder");
>
>
> How should we handle that?
>
> Would you like to ignore them and fix them up as we go along (perfectly
> understandable)? Or should we go through them as part of this rename action?
>
>
> Aha - actually I see evidence that you have fixed some or most of them
> up - so I'll add inline notes when I see any more... <edit, I don't
> think there are any other than the one above perhaps>
>
Yes, I tried to tackle them while renaming, I might have missed some.
Thanks for checking which ones still need to be handled.
>
>
>
> One final point, which I'll leave up to you - could we split the rename
> from the V4L2Device addition? Or is that too painful now?
>
> It would have been nice to see a first patch that did the renames and
> moves. And then a second patch which introduced and moved/removed code
> to the new base class.
>
> Otherwise, it's very hard to see what the new class adds. (due to bulk
> of the patch)
>
> I can 'gloss over' that last point though.
>
>
> <edit: I appreciate how painful this patch is to work with, but if it's
> easy to split - it would add a lot of value. This patch shows a diff
> between V4L2Device and V4L2Videodev, and 'adds' V4L2Videodev instead of
> diffing V4L2Videodev and adding the new V4L2Device>
Splitting might indeed be desirable, and as you've offered to look
into doing that, I surely won't oppose :)
Thanks
j
>
>
>
>
>
> > ---
> >
> > include/libcamera/buffer.h | 2 +-
> > src/libcamera/camera_sensor.cpp | 4 +-
> > src/libcamera/include/camera_sensor.h | 4 +-
> > src/libcamera/include/v4l2_device.h | 170 +--
> > .../{v4l2_subdevice.h => v4l2_subdev.h} | 25 +-
> > src/libcamera/include/v4l2_videodev.h | 183 ++++
> > src/libcamera/meson.build | 6 +-
> > src/libcamera/pipeline/ipu3/ipu3.cpp | 32 +-
> > src/libcamera/pipeline/rkisp1/rkisp1.cpp | 16 +-
> > src/libcamera/pipeline/uvcvideo.cpp | 6 +-
> > src/libcamera/pipeline/vimc.cpp | 6 +-
> > src/libcamera/v4l2_device.cpp | 983 ++---------------
> > .../{v4l2_subdevice.cpp => v4l2_subdev.cpp} | 100 +-
> > src/libcamera/v4l2_videodev.cpp | 992 ++++++++++++++++++
> > test/meson.build | 4 +-
> > .../list_formats.cpp | 6 +-
> > .../meson.build | 8 +-
> > .../test_formats.cpp | 6 +-
> > .../v4l2_subdev_test.cpp} | 12 +-
> > .../v4l2_subdev_test.h} | 10 +-
> > .../buffer_sharing.cpp | 18 +-
> > .../capture_async.cpp | 6 +-
> > .../double_open.cpp | 8 +-
> > .../formats.cpp | 8 +-
> > .../meson.build | 8 +-
> > .../request_buffers.cpp | 6 +-
> > .../stream_on_off.cpp | 6 +-
> > .../v4l2_videodev_test.cpp} | 8 +-
> > .../v4l2_videodev_test.h} | 8 +-
> > 29 files changed, 1397 insertions(+), 1254 deletions(-)
> > rename src/libcamera/include/{v4l2_subdevice.h => v4l2_subdev.h} (67%)
> > create mode 100644 src/libcamera/include/v4l2_videodev.h
> > rename src/libcamera/{v4l2_subdevice.cpp => v4l2_subdev.cpp} (79%)
> > create mode 100644 src/libcamera/v4l2_videodev.cpp
> > rename test/{v4l2_subdevice => v4l2_subdev}/list_formats.cpp (95%)
> > rename test/{v4l2_subdevice => v4l2_subdev}/meson.build (57%)
> > rename test/{v4l2_subdevice => v4l2_subdev}/test_formats.cpp (93%)
> > rename test/{v4l2_subdevice/v4l2_subdevice_test.cpp => v4l2_subdev/v4l2_subdev_test.cpp} (84%)
> > rename test/{v4l2_subdevice/v4l2_subdevice_test.h => v4l2_subdev/v4l2_subdev_test.h} (76%)
> > rename test/{v4l2_device => v4l2_videodev}/buffer_sharing.cpp (90%)
> > rename test/{v4l2_device => v4l2_videodev}/capture_async.cpp (92%)
> > rename test/{v4l2_device => v4l2_videodev}/double_open.cpp (76%)
> > rename test/{v4l2_device => v4l2_videodev}/formats.cpp (86%)
> > rename test/{v4l2_device => v4l2_videodev}/meson.build (75%)
> > rename test/{v4l2_device => v4l2_videodev}/request_buffers.cpp (78%)
> > rename test/{v4l2_device => v4l2_videodev}/stream_on_off.cpp (79%)
> > rename test/{v4l2_device/v4l2_device_test.cpp => v4l2_videodev/v4l2_videodev_test.cpp} (91%)
> > rename test/{v4l2_device/v4l2_device_test.h => v4l2_videodev/v4l2_videodev_test.h} (82%)
> >
> > diff --git a/include/libcamera/buffer.h b/include/libcamera/buffer.h
> > index 8f9b42e39339..7da9a4263fd3 100644
> > --- a/include/libcamera/buffer.h
> > +++ b/include/libcamera/buffer.h
> > @@ -59,7 +59,7 @@ private:
> > friend class BufferPool;
> > friend class PipelineHandler;
> > friend class Request;
> > - friend class V4L2Device;
> > + friend class V4L2Videodev;
> >
> > void cancel();
> >
> > diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
> > index cb6649efac3f..83f121ec3514 100644
> > --- a/src/libcamera/camera_sensor.cpp
> > +++ b/src/libcamera/camera_sensor.cpp
> > @@ -14,7 +14,7 @@
> > #include <math.h>
> >
> > #include "formats.h"
> > -#include "v4l2_subdevice.h"
> > +#include "v4l2_subdev.h"
> >
> > /**
> > * \file camera_sensor.h
> > @@ -48,7 +48,7 @@ LOG_DEFINE_CATEGORY(CameraSensor);
> > CameraSensor::CameraSensor(const MediaEntity *entity)
> > : entity_(entity)
> > {
> > - subdev_ = new V4L2Subdevice(entity);
> > + subdev_ = new V4L2Subdev(entity);
> > }
> >
> > /**
> > diff --git a/src/libcamera/include/camera_sensor.h b/src/libcamera/include/camera_sensor.h
> > index b823480241a7..2dd311652aa5 100644
> > --- a/src/libcamera/include/camera_sensor.h
> > +++ b/src/libcamera/include/camera_sensor.h
> > @@ -17,7 +17,7 @@
> > namespace libcamera {
> >
> > class MediaEntity;
> > -class V4L2Subdevice;
> > +class V4L2Subdev;
> >
> > struct V4L2SubdeviceFormat;
> >
> > @@ -46,7 +46,7 @@ protected:
> >
> > private:
> > const MediaEntity *entity_;
> > - V4L2Subdevice *subdev_;
> > + V4L2Subdev *subdev_;
> >
> > std::vector<unsigned int> mbusCodes_;
> > std::vector<Size> sizes_;
> > diff --git a/src/libcamera/include/v4l2_device.h b/src/libcamera/include/v4l2_device.h
> > index bdecc087fe5a..e3f3ecf235d4 100644
> > --- a/src/libcamera/include/v4l2_device.h
> > +++ b/src/libcamera/include/v4l2_device.h
> > @@ -2,183 +2,37 @@
> > /*
> > * Copyright (C) 2019, Google Inc.
> > *
> > - * v4l2_device.h - V4L2 Device
> > + * v4l2_device.h - Common base for V4L2 video devices and subdevices
> > */
> > #ifndef __LIBCAMERA_V4L2_DEVICE_H__
> > #define __LIBCAMERA_V4L2_DEVICE_H__
> >
> > -#include <atomic>
> > #include <string>
> > -#include <vector>
> > -
> > -#include <linux/videodev2.h>
> > -
> > -#include <libcamera/geometry.h>
> > -#include <libcamera/signal.h>
> > -
> > -#include "log.h"
> >
> > namespace libcamera {
> >
> > -class Buffer;
> > -class BufferPool;
> > -class EventNotifier;
> > -class MediaDevice;
> > -class MediaEntity;
> > -
> > -struct V4L2Capability final : v4l2_capability {
> > - const char *driver() const
> > - {
> > - return reinterpret_cast<const char *>(v4l2_capability::driver);
> > - }
> > - const char *card() const
> > - {
> > - return reinterpret_cast<const char *>(v4l2_capability::card);
> > - }
> > - const char *bus_info() const
> > - {
> > - return reinterpret_cast<const char *>(v4l2_capability::bus_info);
> > - }
> > - unsigned int device_caps() const
> > - {
> > - return capabilities & V4L2_CAP_DEVICE_CAPS
> > - ? v4l2_capability::device_caps
> > - : v4l2_capability::capabilities;
> > - }
> > - bool isMultiplanar() const
> > - {
> > - return device_caps() & (V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > - V4L2_CAP_VIDEO_OUTPUT_MPLANE);
> > - }
> > - bool isCapture() const
> > - {
> > - return device_caps() & (V4L2_CAP_VIDEO_CAPTURE |
> > - V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > - V4L2_CAP_META_CAPTURE);
> > - }
> > - bool isOutput() const
> > - {
> > - return device_caps() & (V4L2_CAP_VIDEO_OUTPUT |
> > - V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> > - V4L2_CAP_META_OUTPUT);
> > - }
> > - bool isVideo() const
> > - {
> > - return device_caps() & (V4L2_CAP_VIDEO_CAPTURE |
> > - V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > - V4L2_CAP_VIDEO_OUTPUT |
> > - V4L2_CAP_VIDEO_OUTPUT_MPLANE);
> > - }
> > - bool isMeta() const
> > - {
> > - return device_caps() & (V4L2_CAP_META_CAPTURE |
> > - V4L2_CAP_META_OUTPUT);
> > - }
> > - bool isVideoCapture() const
> > - {
> > - return isVideo() && isCapture();
> > - }
> > - bool isVideoOutput() const
> > - {
> > - return isVideo() && isOutput();
> > - }
> > - bool isMetaCapture() const
> > - {
> > - return isMeta() && isCapture();
> > - }
> > - bool isMetaOutput() const
> > - {
> > - return isMeta() && isOutput();
> > - }
> > - bool hasStreaming() const
> > - {
> > - return device_caps() & V4L2_CAP_STREAMING;
> > - }
> > -};
> > -
> > -class V4L2DeviceFormat
> > +class V4L2Device
> > {
> > public:
> > - uint32_t fourcc;
> > - Size size;
> > -
> > - struct {
> > - uint32_t size;
> > - uint32_t bpl;
> > - } planes[3];
> > - unsigned int planesCount;
> > -
> > - const std::string toString() const;
> > -};
> > + virtual ~V4L2Device()
> > + {
> > + }
> >
> > -class V4L2Device : protected Loggable
> > -{
> > -public:
> > - explicit V4L2Device(const std::string &deviceNode);
> > - explicit V4L2Device(const MediaEntity *entity);
> > - V4L2Device(const V4L2Device &) = delete;
> > - ~V4L2Device();
> > +protected:
> > + V4L2Device();
> >
> > - V4L2Device &operator=(const V4L2Device &) = delete;
> > + int fd() { return fd_; }
> >
> > - int open();
> > + int open(const std::string &pathname, unsigned int flags);
> > + int close();
> > bool isOpen() const;
> > - void close();
> > -
> > - const char *driverName() const { return caps_.driver(); }
> > - const char *deviceName() const { return caps_.card(); }
> > - const char *busName() const { return caps_.bus_info(); }
> > - const std::string &deviceNode() const { return deviceNode_; }
> >
> > - int getFormat(V4L2DeviceFormat *format);
> > - int setFormat(V4L2DeviceFormat *format);
> > -
> > - int exportBuffers(BufferPool *pool);
> > - int importBuffers(BufferPool *pool);
> > - int releaseBuffers();
> > -
> > - int queueBuffer(Buffer *buffer);
> > - Signal<Buffer *> bufferReady;
> > -
> > - int streamOn();
> > - int streamOff();
> > -
> > - static V4L2Device *fromEntityName(const MediaDevice *media,
> > - const std::string &entity);
> > -
> > -protected:
> > - std::string logPrefix() const;
> > + int ioctl(unsigned long request, void *argp);
> >
> > private:
> > - int getFormatMeta(V4L2DeviceFormat *format);
> > - int setFormatMeta(V4L2DeviceFormat *format);
> > -
> > - int getFormatMultiplane(V4L2DeviceFormat *format);
> > - int setFormatMultiplane(V4L2DeviceFormat *format);
> > -
> > - int getFormatSingleplane(V4L2DeviceFormat *format);
> > - int setFormatSingleplane(V4L2DeviceFormat *format);
> > -
> > - int requestBuffers(unsigned int count);
> > - int createPlane(Buffer *buffer, unsigned int plane,
> > - unsigned int length);
> > -
> > - Buffer *dequeueBuffer();
> > - void bufferAvailable(EventNotifier *notifier);
> > -
> > - std::string deviceNode_;
> > int fd_;
> > - V4L2Capability caps_;
> > -
> > - enum v4l2_buf_type bufferType_;
> > - enum v4l2_memory memoryType_;
> > -
> > - BufferPool *bufferPool_;
> > - std::atomic<unsigned int> queuedBuffersCount_;
> > -
> > - EventNotifier *fdEvent_;
> > };
> >
> > -} /* namespace libcamera */
> > +}; /* namespace libcamera */
> >
> > #endif /* __LIBCAMERA_V4L2_DEVICE_H__ */
> > diff --git a/src/libcamera/include/v4l2_subdevice.h b/src/libcamera/include/v4l2_subdev.h
> > similarity index 67%
> > rename from src/libcamera/include/v4l2_subdevice.h
> > rename to src/libcamera/include/v4l2_subdev.h
> > index 3e4e5107aebe..9770f05deae4 100644
> > --- a/src/libcamera/include/v4l2_subdevice.h
> > +++ b/src/libcamera/include/v4l2_subdev.h
> > @@ -2,10 +2,10 @@
> > /*
> > * Copyright (C) 2019, Google Inc.
> > *
> > - * v4l2_subdevice.h - V4L2 Subdevice
> > + * v4l2_subdev.h - V4L2 Subdevice
> > */
> > -#ifndef __LIBCAMERA_V4L2_SUBDEVICE_H__
> > -#define __LIBCAMERA_V4L2_SUBDEVICE_H__
> > +#ifndef __LIBCAMERA_V4L2_SUBDEV_H__
> > +#define __LIBCAMERA_V4L2_SUBDEV_H__
> >
> > #include <map>
> > #include <string>
> > @@ -16,6 +16,7 @@
> > #include "formats.h"
> > #include "log.h"
> > #include "media_object.h"
> > +#include "v4l2_device.h"
> >
> > namespace libcamera {
> >
> > @@ -28,16 +29,15 @@ struct V4L2SubdeviceFormat {
> > const std::string toString() const;
> > };
> >
> > -class V4L2Subdevice : protected Loggable
> > +class V4L2Subdev : public V4L2Device, protected Loggable
> > {
> > public:
> > - explicit V4L2Subdevice(const MediaEntity *entity);
> > - V4L2Subdevice(const V4L2Subdevice &) = delete;
> > - V4L2Subdevice &operator=(const V4L2Subdevice &) = delete;
> > - ~V4L2Subdevice();
> > + explicit V4L2Subdev(const MediaEntity *entity);
> > + V4L2Subdev(const V4L2Subdev &) = delete;
> > + V4L2Subdev &operator=(const V4L2Subdev &) = delete;
> > + ~V4L2Subdev();
> >
> > int open();
> > - bool isOpen() const;
> > void close();
> >
> > const MediaEntity *entity() const { return entity_; }
> > @@ -50,8 +50,8 @@ public:
> > int getFormat(unsigned int pad, V4L2SubdeviceFormat *format);
> > int setFormat(unsigned int pad, V4L2SubdeviceFormat *format);
> >
> > - static V4L2Subdevice *fromEntityName(const MediaDevice *media,
> > - const std::string &entity);
> > + static V4L2Subdev *fromEntityName(const MediaDevice *media,
> > + const std::string &entity);
> >
> > protected:
> > std::string logPrefix() const;
> > @@ -64,9 +64,8 @@ private:
> > Rectangle *rect);
> >
> > const MediaEntity *entity_;
> > - int fd_;
> > };
> >
> > } /* namespace libcamera */
> >
> > -#endif /* __LIBCAMERA_V4L2_SUBDEVICE_H__ */
> > +#endif /* __LIBCAMERA_V4L2_SUBDEV_H__ */
> > diff --git a/src/libcamera/include/v4l2_videodev.h b/src/libcamera/include/v4l2_videodev.h
> > new file mode 100644
> > index 000000000000..c072f1fa5449
> > --- /dev/null
> > +++ b/src/libcamera/include/v4l2_videodev.h
> > @@ -0,0 +1,183 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * v4l2_videodev.h - V4L2 Video Device
> > + */
> > +#ifndef __LIBCAMERA_V4L2_VIDEODEV_H__
> > +#define __LIBCAMERA_V4L2_VIDEODEV_H__
> > +
> > +#include <atomic>
> > +#include <string>
> > +#include <vector>
> > +
> > +#include <linux/videodev2.h>
> > +
> > +#include <libcamera/geometry.h>
> > +#include <libcamera/signal.h>
> > +
> > +#include "log.h"
> > +#include "v4l2_device.h"
> > +
> > +namespace libcamera {
> > +
> > +class Buffer;
> > +class BufferPool;
> > +class EventNotifier;
> > +class MediaDevice;
> > +class MediaEntity;
> > +
> > +struct V4L2Capability final : v4l2_capability {
> > + const char *driver() const
> > + {
> > + return reinterpret_cast<const char *>(v4l2_capability::driver);
> > + }
> > + const char *card() const
> > + {
> > + return reinterpret_cast<const char *>(v4l2_capability::card);
> > + }
> > + const char *bus_info() const
> > + {
> > + return reinterpret_cast<const char *>(v4l2_capability::bus_info);
> > + }
> > + unsigned int device_caps() const
> > + {
> > + return capabilities & V4L2_CAP_DEVICE_CAPS
> > + ? v4l2_capability::device_caps
> > + : v4l2_capability::capabilities;
> > + }
> > + bool isMultiplanar() const
> > + {
> > + return device_caps() & (V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > + V4L2_CAP_VIDEO_OUTPUT_MPLANE);
> > + }
> > + bool isCapture() const
> > + {
> > + return device_caps() & (V4L2_CAP_VIDEO_CAPTURE |
> > + V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > + V4L2_CAP_META_CAPTURE);
> > + }
> > + bool isOutput() const
> > + {
> > + return device_caps() & (V4L2_CAP_VIDEO_OUTPUT |
> > + V4L2_CAP_VIDEO_OUTPUT_MPLANE |
> > + V4L2_CAP_META_OUTPUT);
> > + }
> > + bool isVideo() const
> > + {
> > + return device_caps() & (V4L2_CAP_VIDEO_CAPTURE |
> > + V4L2_CAP_VIDEO_CAPTURE_MPLANE |
> > + V4L2_CAP_VIDEO_OUTPUT |
> > + V4L2_CAP_VIDEO_OUTPUT_MPLANE);
> > + }
> > + bool isMeta() const
> > + {
> > + return device_caps() & (V4L2_CAP_META_CAPTURE |
> > + V4L2_CAP_META_OUTPUT);
> > + }
> > + bool isVideoCapture() const
> > + {
> > + return isVideo() && isCapture();
> > + }
> > + bool isVideoOutput() const
> > + {
> > + return isVideo() && isOutput();
> > + }
> > + bool isMetaCapture() const
> > + {
> > + return isMeta() && isCapture();
> > + }
> > + bool isMetaOutput() const
> > + {
> > + return isMeta() && isOutput();
> > + }
> > + bool hasStreaming() const
> > + {
> > + return device_caps() & V4L2_CAP_STREAMING;
> > + }
> > +};
> > +
> > +class V4L2DeviceFormat
> > +{
> > +public:
> > + uint32_t fourcc;
> > + Size size;
> > +
> > + struct {
> > + uint32_t size;
> > + uint32_t bpl;
> > + } planes[3];
> > + unsigned int planesCount;
> > +
> > + const std::string toString() const;
> > +};
> > +
> > +class V4L2Videodev : public V4L2Device, protected Loggable
> > +{
> > +public:
> > + explicit V4L2Videodev(const std::string &deviceNode);
> > + explicit V4L2Videodev(const MediaEntity *entity);
> > + V4L2Videodev(const V4L2Videodev &) = delete;
> > + ~V4L2Videodev();
> > +
> > + V4L2Videodev &operator=(const V4L2Videodev &) = delete;
> > +
> > + int open();
> > + void close();
> > +
> > + const char *driverName() const { return caps_.driver(); }
> > + const char *deviceName() const { return caps_.card(); }
> > + const char *busName() const { return caps_.bus_info(); }
> > + const std::string &deviceNode() const { return deviceNode_; }
> > +
> > + int getFormat(V4L2DeviceFormat *format);
> > + int setFormat(V4L2DeviceFormat *format);
> > +
> > + int exportBuffers(BufferPool *pool);
> > + int importBuffers(BufferPool *pool);
> > + int releaseBuffers();
> > +
> > + int queueBuffer(Buffer *buffer);
> > + Signal<Buffer *> bufferReady;
> > +
> > + int streamOn();
> > + int streamOff();
> > +
> > + static V4L2Videodev *fromEntityName(const MediaDevice *media,
> > + const std::string &entity);
> > +
> > +protected:
> > + std::string logPrefix() const;
> > +
> > +private:
> > + int getFormatMeta(V4L2DeviceFormat *format);
> > + int setFormatMeta(V4L2DeviceFormat *format);
> > +
> > + int getFormatMultiplane(V4L2DeviceFormat *format);
> > + int setFormatMultiplane(V4L2DeviceFormat *format);
> > +
> > + int getFormatSingleplane(V4L2DeviceFormat *format);
> > + int setFormatSingleplane(V4L2DeviceFormat *format);
> > +
> > + int requestBuffers(unsigned int count);
> > + int createPlane(Buffer *buffer, unsigned int plane,
> > + unsigned int length);
> > +
> > + Buffer *dequeueBuffer();
> > + void bufferAvailable(EventNotifier *notifier);
> > +
> > + std::string deviceNode_;
> > + V4L2Capability caps_;
> > +
> > + enum v4l2_buf_type bufferType_;
> > + enum v4l2_memory memoryType_;
> > +
> > + BufferPool *bufferPool_;
> > + std::atomic<unsigned int> queuedBuffersCount_;
> > +
> > + EventNotifier *fdEvent_;
> > +};
> > +
> > +} /* namespace libcamera */
> > +
> > +#endif /* __LIBCAMERA_V4L2_VIDEODEV_H__ */
> > diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
> > index 1ca1083cf5c7..f7a48fcb43df 100644
> > --- a/src/libcamera/meson.build
> > +++ b/src/libcamera/meson.build
> > @@ -24,7 +24,8 @@ libcamera_sources = files([
> > 'timer.cpp',
> > 'utils.cpp',
> > 'v4l2_device.cpp',
> > - 'v4l2_subdevice.cpp',
> > + 'v4l2_subdev.cpp',
> > + 'v4l2_videodev.cpp',
> > ])
> >
> > libcamera_headers = files([
> > @@ -42,7 +43,8 @@ libcamera_headers = files([
> > 'include/pipeline_handler.h',
> > 'include/utils.h',
> > 'include/v4l2_device.h',
> > - 'include/v4l2_subdevice.h',
> > + 'include/v4l2_subdev.h',
> > + 'include/v4l2_videodev.h',
> > ])
> >
> > libcamera_internal_includes = include_directories('include')
> > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > index f2bdecbac20f..c4229453afe8 100644
> > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > @@ -22,8 +22,8 @@
> > #include "media_device.h"
> > #include "pipeline_handler.h"
> > #include "utils.h"
> > -#include "v4l2_device.h"
> > -#include "v4l2_subdevice.h"
> > +#include "v4l2_subdev.h"
> > +#include "v4l2_videodev.h"
> >
> > namespace libcamera {
> >
> > @@ -39,7 +39,7 @@ public:
> >
> > /* ImgU output descriptor: group data specific to an ImgU output. */
> > struct ImgUOutput {
> > - V4L2Device *dev;
> > + V4L2Videodev *dev;
> > unsigned int pad;
> > std::string name;
> > BufferPool *pool;
> > @@ -84,8 +84,8 @@ public:
> > std::string name_;
> > MediaDevice *media_;
> >
> > - V4L2Subdevice *imgu_;
> > - V4L2Device *input_;
> > + V4L2Subdev *imgu_;
> > + V4L2Videodev *input_;
> > ImgUOutput output_;
> > ImgUOutput viewfinder_;
> > ImgUOutput stat_;
> > @@ -125,8 +125,8 @@ public:
> >
> > static int mediaBusToFormat(unsigned int code);
> >
> > - V4L2Device *output_;
> > - V4L2Subdevice *csi2_;
> > + V4L2Videodev *output_;
> > + V4L2Subdev *csi2_;
> > CameraSensor *sensor_;
> >
> > BufferPool pool_;
> > @@ -943,7 +943,7 @@ void IPU3CameraData::cio2BufferReady(Buffer *buffer)
> > * Create and open the V4L2 devices and subdevices of the ImgU instance
> > * with \a index.
> > *
> > - * In case of errors the created V4L2Device and V4L2Subdevice instances
> > + * In case of errors the created V4L2Videodev and V4L2Subdev instances
> > * are destroyed at pipeline handler delete time.
> > *
> > * \return 0 on success or a negative error code otherwise
> > @@ -961,17 +961,17 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index)
> > * by the match() function: no need to check for newly created
> > * video devices and subdevice validity here.
> > */
> > - imgu_ = V4L2Subdevice::fromEntityName(media, name_);
> > + imgu_ = V4L2Subdev::fromEntityName(media, name_);
> > ret = imgu_->open();
> > if (ret)
> > return ret;
> >
> > - input_ = V4L2Device::fromEntityName(media, name_ + " input");
> > + input_ = V4L2Videodev::fromEntityName(media, name_ + " input");
> > ret = input_->open();
> > if (ret)
> > return ret;
> >
> > - output_.dev = V4L2Device::fromEntityName(media, name_ + " output");
> > + output_.dev = V4L2Videodev::fromEntityName(media, name_ + " output");
> > ret = output_.dev->open();
> > if (ret)
> > return ret;
> > @@ -980,7 +980,7 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index)
> > output_.name = "output";
> > output_.pool = &outPool_;
> >
> > - viewfinder_.dev = V4L2Device::fromEntityName(media,
> > + viewfinder_.dev = V4L2Videodev::fromEntityName(media,
> > name_ + " viewfinder");
>
> alignment issue.
>
> > ret = viewfinder_.dev->open();
> > if (ret)
> > @@ -990,7 +990,7 @@ int ImgUDevice::init(MediaDevice *media, unsigned int index)
> > viewfinder_.name = "viewfinder";
> > viewfinder_.pool = &vfPool_;
> >
> > - stat_.dev = V4L2Device::fromEntityName(media, name_ + " 3a stat");
> > + stat_.dev = V4L2Videodev::fromEntityName(media, name_ + " 3a stat");
> > ret = stat_.dev->open();
> > if (ret)
> > return ret;
> > @@ -1067,7 +1067,7 @@ int ImgUDevice::configureInput(const Size &size,
> > int ImgUDevice::configureOutput(ImgUOutput *output,
> > const StreamConfiguration &cfg)
> > {
> > - V4L2Device *dev = output->dev;
> > + V4L2Videodev *dev = output->dev;
> > unsigned int pad = output->pad;
> >
> > V4L2SubdeviceFormat imguFormat = {};
> > @@ -1331,13 +1331,13 @@ int CIO2Device::init(const MediaDevice *media, unsigned int index)
> > * might impact on power consumption.
> > */
> >
> > - csi2_ = new V4L2Subdevice(csi2Entity);
> > + csi2_ = new V4L2Subdev(csi2Entity);
> > ret = csi2_->open();
> > if (ret)
> > return ret;
> >
> > std::string cio2Name = "ipu3-cio2 " + std::to_string(index);
> > - output_ = V4L2Device::fromEntityName(media, cio2Name);
> > + output_ = V4L2Videodev::fromEntityName(media, cio2Name);
> > ret = output_->open();
> > if (ret)
> > return ret;
> > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > index 9b3eea2f6dd3..08b640a1bd49 100644
> > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > @@ -23,8 +23,8 @@
> > #include "media_device.h"
> > #include "pipeline_handler.h"
> > #include "utils.h"
> > -#include "v4l2_device.h"
> > -#include "v4l2_subdevice.h"
> > +#include "v4l2_subdev.h"
> > +#include "v4l2_videodev.h"
> >
> > namespace libcamera {
> >
> > @@ -104,9 +104,9 @@ private:
> > void bufferReady(Buffer *buffer);
> >
> > MediaDevice *media_;
> > - V4L2Subdevice *dphy_;
> > - V4L2Subdevice *isp_;
> > - V4L2Device *video_;
> > + V4L2Subdev *dphy_;
> > + V4L2Subdev *isp_;
> > + V4L2Videodev *video_;
> >
> > Camera *activeCamera_;
> > };
> > @@ -449,16 +449,16 @@ bool PipelineHandlerRkISP1::match(DeviceEnumerator *enumerator)
> > return false;
> >
> > /* Create the V4L2 subdevices we will need. */
> > - dphy_ = V4L2Subdevice::fromEntityName(media_, "rockchip-sy-mipi-dphy");
> > + dphy_ = V4L2Subdev::fromEntityName(media_, "rockchip-sy-mipi-dphy");
> > if (dphy_->open() < 0)
> > return false;
> >
> > - isp_ = V4L2Subdevice::fromEntityName(media_, "rkisp1-isp-subdev");
> > + isp_ = V4L2Subdev::fromEntityName(media_, "rkisp1-isp-subdev");
> > if (isp_->open() < 0)
> > return false;
> >
> > /* Locate and open the capture video node. */
> > - video_ = V4L2Device::fromEntityName(media_, "rkisp1_mainpath");
> > + video_ = V4L2Videodev::fromEntityName(media_, "rkisp1_mainpath");
> > if (video_->open() < 0)
> > return false;
> >
> > diff --git a/src/libcamera/pipeline/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo.cpp
> > index 45260f34c8f5..fa3c5a2f8374 100644
> > --- a/src/libcamera/pipeline/uvcvideo.cpp
> > +++ b/src/libcamera/pipeline/uvcvideo.cpp
> > @@ -14,7 +14,7 @@
> > #include "media_device.h"
> > #include "pipeline_handler.h"
> > #include "utils.h"
> > -#include "v4l2_device.h"
> > +#include "v4l2_videodev.h"
> >
> > namespace libcamera {
> >
> > @@ -35,7 +35,7 @@ public:
> >
> > void bufferReady(Buffer *buffer);
> >
> > - V4L2Device *video_;
> > + V4L2Videodev *video_;
> > Stream stream_;
> > };
> >
> > @@ -229,7 +229,7 @@ bool PipelineHandlerUVC::match(DeviceEnumerator *enumerator)
> > /* Locate and open the default video node. */
> > for (MediaEntity *entity : media->entities()) {
> > if (entity->flags() & MEDIA_ENT_FL_DEFAULT) {
> > - data->video_ = new V4L2Device(entity);
> > + data->video_ = new V4L2Videodev(entity);
> > break;
> > }
> > }
> > diff --git a/src/libcamera/pipeline/vimc.cpp b/src/libcamera/pipeline/vimc.cpp
> > index ad4577acd9d6..e1fa3eabeae0 100644
> > --- a/src/libcamera/pipeline/vimc.cpp
> > +++ b/src/libcamera/pipeline/vimc.cpp
> > @@ -20,7 +20,7 @@
> > #include "media_device.h"
> > #include "pipeline_handler.h"
> > #include "utils.h"
> > -#include "v4l2_device.h"
> > +#include "v4l2_videodev.h"
> >
> > namespace libcamera {
> >
> > @@ -41,7 +41,7 @@ public:
> >
> > void bufferReady(Buffer *buffer);
> >
> > - V4L2Device *video_;
> > + V4L2Videodev *video_;
> > Stream stream_;
> > };
> >
> > @@ -262,7 +262,7 @@ bool PipelineHandlerVimc::match(DeviceEnumerator *enumerator)
> > std::unique_ptr<VimcCameraData> data = utils::make_unique<VimcCameraData>(this);
> >
> > /* Locate and open the capture video node. */
> > - data->video_ = new V4L2Device(media->getEntityByName("Raw Capture 1"));
> > + data->video_ = new V4L2Videodev(media->getEntityByName("Raw Capture 1"));
> > if (data->video_->open())
> > return false;
> >
> > diff --git a/src/libcamera/v4l2_device.cpp b/src/libcamera/v4l2_device.cpp
> > index 0821bd75fb42..c20dc7928fcd 100644
> > --- a/src/libcamera/v4l2_device.cpp
> > +++ b/src/libcamera/v4l2_device.cpp
> > @@ -2,1011 +2,150 @@
> > /*
> > * Copyright (C) 2019, Google Inc.
> > *
> > - * v4l2_device.cpp - V4L2 Device
> > + * v4l2_device.cpp - Common base for V4L2 devices and subdevices
> > */
> >
> > #include "v4l2_device.h"
> >
> > #include <fcntl.h>
> > -#include <iomanip>
> > -#include <sstream>
> > #include <string.h>
> > #include <sys/ioctl.h>
> > -#include <sys/mman.h>
> > -#include <sys/time.h>
> > #include <unistd.h>
> > -#include <vector>
> > -
> > -#include <libcamera/buffer.h>
> > -#include <libcamera/event_notifier.h>
> >
> > #include "log.h"
> > -#include "media_device.h"
> > -#include "media_object.h"
> >
> > /**
> > * \file v4l2_device.h
> > - * \brief V4L2 Device API
> > + * \brief Common base for V4L2 devices and subdevices
> > */
> > +
> > namespace libcamera {
> >
> > LOG_DEFINE_CATEGORY(V4L2)
> >
> > /**
> > - * \struct V4L2Capability
> > - * \brief struct v4l2_capability object wrapper and helpers
> > - *
> > - * The V4L2Capability structure manages the information returned by the
> > - * VIDIOC_QUERYCAP ioctl.
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::driver()
> > - * \brief Retrieve the driver module name
> > - * \return The string containing the name of the driver module
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::card()
> > - * \brief Retrieve the device card name
> > - * \return The string containing the device name
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::bus_info()
> > - * \brief Retrieve the location of the device in the system
> > - * \return The string containing the device location
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::device_caps()
> > - * \brief Retrieve the capabilities of the device
> > - * \return The device specific capabilities if V4L2_CAP_DEVICE_CAPS is set or
> > - * driver capabilities otherwise
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isMultiplanar()
> > - * \brief Identify if the device implements the V4L2 multiplanar APIs
> > - * \return True if the device supports multiplanar APIs
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isCapture()
> > - * \brief Identify if the device captures data
> > - * \return True if the device can capture data
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isOutput()
> > - * \brief Identify if the device outputs data
> > - * \return True if the device can output data
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isVideo()
> > - * \brief Identify if the device captures or outputs images
> > - * \return True if the device can capture or output images
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isMeta()
> > - * \brief Identify if the device captures or outputs image meta-data
> > - *
> > - * \todo Add support for META_CAPTURE introduced in Linux v5.0
> > - *
> > - * \return True if the device can capture or output image meta-data
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isVideoCapture()
> > - * \brief Identify if the device captures images
> > - * \return True if the device can capture images
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isVideoOutput()
> > - * \brief Identify if the device outputs images
> > - * \return True if the device can output images
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isMetaCapture()
> > - * \brief Identify if the device captures image meta-data
> > - * \return True if the device can capture image meta-data
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::isMetaOutput()
> > - * \brief Identify if the device outputs image meta-data
> > - * \return True if the device can output image meta-data
> > - */
> > -
> > -/**
> > - * \fn V4L2Capability::hasStreaming()
> > - * \brief Determine if the device can perform Streaming I/O
> > - * \return True if the device provides Streaming I/O IOCTLs
> > - */
> > -
> > -/**
> > - * \class V4L2DeviceFormat
> > - * \brief The V4L2 device image format and sizes
> > - *
> > - * This class describes the image format and resolution to be programmed on a
> > - * V4L2 video device. The image format is defined by a fourcc code (as specified
> > - * by the V4L2 API with the V4L2_PIX_FMT_* macros), a resolution (width and
> > - * height) and one to three planes with configurable line stride and a total
> > - * per-plane size in bytes.
> > - *
> > - * Image formats, as defined by the V4L2 APIs, are categorised as packed,
> > - * semi-planar and planar, and describe the layout of the image pixel components
> > - * stored in memory.
> > - *
> > - * Packed image formats store pixel components one after the other, in a
> > - * contiguous memory area. Examples of packed image formats are YUYV
> > - * permutations, RGB with different pixel sub-sampling ratios such as RGB565 or
> > - * RGB666 or Raw-Bayer formats such as SRGGB8 or SGRBG12.
> > - *
> > - * Semi-planar and planar image formats store the pixel components in separate
> > - * and possibly non-contiguous memory areas, named planes, whose sizes depend on
> > - * the pixel components sub-sampling ratios, which are defined by the format.
> > - * Semi-planar formats use two planes to store pixel components and notable
> > - * examples of such formats are the NV12 and NV16 formats, while planar formats
> > - * use three planes to store pixel components and notable examples are YUV422
> > - * and YUV420.
> > - *
> > - * Image formats supported by the V4L2 API are defined and described in Section
> > - * number 2 of the "Part I - Video for Linux API" chapter of the "Linux Media
> > - * Infrastructure userspace API", part of the Linux kernel documentation.
> > - *
> > - * In the context of this document, packed image formats are referred to as
> > - * "packed formats" and semi-planar and planar image formats are referred to as
> > - * "planar formats".
> > - *
> > - * V4L2 also defines two different sets of APIs to work with devices that store
> > - * planes in contiguous or separate memory areas. They are named "Single-plane
> > - * APIs" and "Multi-plane APIs" respectively and are documented in Section 2.1
> > - * and Section 2.2 of the above mentioned "Part I - Video for Linux API"
> > - * documentation.
> > - *
> > - * The single-plane API allows, among other parameters, the configuration of the
> > - * image resolution, the pixel format and the stride length. In that case the
> > - * stride applies to all planes (possibly sub-sampled). The multi-plane API
> > - * allows configuring the resolution, the pixel format and a per-plane stride
> > - * length and total size.
> > + * \class V4L2Device
> > + * \brief Base class for V4L2Videodev and V4L2Subdev
> > *
> > - * Packed image formats, which occupy a single memory area, are easily described
> > - * through the single-plane API. When used on a device that implements the
> > - * multi-plane API, only the size and stride information contained in the first
> > - * plane are taken into account.
> > + * The V4L2Device class groups together the methods and fields common to
> > + * both V4L2Videodev and V4L2Subdev, and provide a base class which
> > + * provides methods to open and close the device node associated with the
> > + * device and to perform IOCTL system calls on it.
> > *
> > - * Planar image formats, which occupy distinct memory areas, are easily
> > - * described through the multi-plane APIs. When used on a device that implements
> > - * the single-plane API, all planes are stored one after the other in a
> > - * contiguous memory area, and it is not possible to configure per-plane stride
> > - * length and size, but only a global stride length which is applied to all
> > - * planes.
> > + * The V4L2Device class cannot be instantiated directly, as its constructor
> > + * is protected. Users should use instead one the derived classes to model
> > + * either a V4L2 video device or a V4L2 subdevice.
> > *
> > - * The V4L2DeviceFormat class describes both packed and planar image formats,
> > - * regardless of the API type (single or multi plane) implemented by the device
> > - * the format has to be applied to. The total size and bytes per line of images
> > - * represented with packed formats are configured using the first entry of the
> > - * V4L2DeviceFormat::planes array, while the per-plane size and per-plane stride
> > - * length of images represented with planar image formats are configured using
> > - * the opportune number of entries of the V4L2DeviceFormat::planes array, as
> > - * prescribed by the image format definition (semi-planar formats use 2 entries,
> > - * while planar formats use the whole 3 entries). The number of valid entries of
> > - * the V4L2DeviceFormat::planes array is defined by the
> > - * V4L2DeviceFormat::planesCount value.
> > - */
> > -
> > -/**
> > - * \var V4L2DeviceFormat::size
> > - * \brief The image size in pixels
> > + * Methods common to V4L2Videodev and V4L2Subdev, such as V4L2 controls
> > + * support are implemented in the base class to maximize code re-use.
> > */
> >
> > /**
> > - * \var V4L2DeviceFormat::fourcc
> > - * \brief The fourcc code describing the pixel encoding scheme
> > + * \brief Construct a V4L2Device
> > *
> > - * The fourcc code, as defined by the V4L2 API with the V4L2_PIX_FMT_* macros,
> > - * that identifies the image format pixel encoding scheme.
> > - */
> > -
> > -/**
> > - * \var V4L2DeviceFormat::planes
> > - * \brief The per-plane memory size information
> > + * The V4L2Device constructor is protected and can only be accessed by the
> > + * V4L2Videodev and V4L2Subdev derived classes.
> > *
> > - * Images are stored in memory in one or more data planes. Each data plane has a
> > - * specific line stride and memory size, which could differ from the image
> > - * visible sizes to accommodate padding at the end of lines and end of planes.
> > - * Only the first \ref planesCount entries are considered valid.
> > - */
> > -
> > -/**
> > - * \var V4L2DeviceFormat::planesCount
> > - * \brief The number of valid data planes
> > + * Initialize the file descriptor to -1.
> > */
> > -
> > -/**
> > - * \brief Assemble and return a string describing the format
> > - * \return A string describing the V4L2DeviceFormat
> > - */
> > -const std::string V4L2DeviceFormat::toString() const
> > +V4L2Device::V4L2Device() :
> > + fd_(-1)
> > {
> > - std::stringstream ss;
> > -
> > - ss.fill(0);
> > - ss << size.toString() << "-0x" << std::hex << std::setw(8) << fourcc;
> > -
> > - return ss.str();
> > }
> >
> > /**
> > - * \class V4L2Device
> > - * \brief V4L2Device object and API
> > - *
> > - * The V4L2 Device API class models an instance of a V4L2 device node.
> > - * It is constructed with the path to a V4L2 video device node. The device node
> > - * is only opened upon a call to open() which must be checked for success.
> > - *
> > - * The device capabilities are validated when the device is opened and the
> > - * device is rejected if it is not a suitable V4L2 capture or output device, or
> > - * if the device does not support streaming I/O.
> > - *
> > - * No API call other than open(), isOpen() and close() shall be called on an
> > - * unopened device instance.
> > - *
> > - * The V4L2Device class tracks queued buffers and handles buffer events. It
> > - * automatically dequeues completed buffers and emits the \ref bufferReady
> > - * signal.
> > - *
> > - * Upon destruction any device left open will be closed, and any resources
> > - * released.
> > - */
> > -
> > -/**
> > - * \brief Construct a V4L2Device
> > - * \param[in] deviceNode The file-system path to the video device node
> > + * \fn V4L2Device::fd()
> > + * \brief Retrieve the V4L2 device file descriptor
> > + * \return The V4L2 device file descriptor, -1 if the device node is not open
> > */
> > -V4L2Device::V4L2Device(const std::string &deviceNode)
> > - : deviceNode_(deviceNode), fd_(-1), bufferPool_(nullptr),
> > - queuedBuffersCount_(0), fdEvent_(nullptr)
> > -{
> > - /*
> > - * We default to an MMAP based CAPTURE device, however this will be
> > - * updated based upon the device capabilities.
> > - */
> > - bufferType_ = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > - memoryType_ = V4L2_MEMORY_MMAP;
> > -}
> >
> > /**
> > - * \brief Construct a V4L2Device from a MediaEntity
> > - * \param[in] entity The MediaEntity to build the device from
> > + * \brief Open a V4L2 device node
> > + * \param pathname The filesystem path of the device node to open
> > + * \param flags Access mode flags
> > + *
> > + * Initialize the file descriptor, which was initially set to -1.
> > *
> > - * Construct a V4L2Device from a MediaEntity's device node path.
> > - */
> > -V4L2Device::V4L2Device(const MediaEntity *entity)
> > - : V4L2Device(entity->deviceNode())
> > -{
> > -}
> > -
> > -V4L2Device::~V4L2Device()
> > -{
> > - close();
> > -}
> > -
> > -/**
> > - * \brief Open a V4L2 device and query its capabilities
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Device::open()
> > +int V4L2Device::open(const std::string &pathname, unsigned int flags)
> > {
> > - int ret;
> > -
> > if (isOpen()) {
> > LOG(V4L2, Error) << "Device already open";
> > return -EBUSY;
> > }
> >
> > - ret = ::open(deviceNode_.c_str(), O_RDWR | O_NONBLOCK);
> > + int ret = ::open(pathname.c_str(), flags);
> > if (ret < 0) {
> > ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to open V4L2 device: " << strerror(-ret);
> > + LOG(V4L2, Error) << "Failed to open V4L2 device: "
> > + << strerror(-ret);
> > return ret;
> > }
> > - fd_ = ret;
> > -
> > - ret = ioctl(fd_, VIDIOC_QUERYCAP, &caps_);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to query device capabilities: "
> > - << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - LOG(V4L2, Debug)
> > - << "Opened device " << caps_.bus_info() << ": "
> > - << caps_.driver() << ": " << caps_.card();
> > -
> > - 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 devices
> > - * (POLLIN), and write notifications for OUTPUT 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;
> > - }
> > -
> > - fdEvent_->activated.connect(this, &V4L2Device::bufferAvailable);
> > - fdEvent_->setEnabled(false);
> > -
> > - return 0;
> > -}
> > -
> > -/**
> > - * \brief Check if device is successfully opened
> > - * \return True if the device is open, false otherwise
> > - */
> > -bool V4L2Device::isOpen() const
> > -{
> > - return fd_ != -1;
> > -}
> > -
> > -/**
> > - * \brief Close the device, releasing any resources acquired by open()
> > - */
> > -void V4L2Device::close()
> > -{
> > - if (fd_ < 0)
> > - return;
> > -
> > - releaseBuffers();
> > - delete fdEvent_;
> > -
> > - ::close(fd_);
> > - fd_ = -1;
> > -}
> > -
> > -/**
> > - * \fn V4L2Device::driverName()
> > - * \brief Retrieve the name of the V4L2 device driver
> > - * \return The string containing the driver name
> > - */
> > -
> > -/**
> > - * \fn V4L2Device::deviceName()
> > - * \brief Retrieve the name of the V4L2 device
> > - * \return The string containing the device name
> > - */
> > -
> > -/**
> > - * \fn V4L2Device::busName()
> > - * \brief Retrieve the location of the device in the system
> > - * \return The string containing the device location
> > - */
> > -
> > -/**
> > - * \fn V4L2Device::deviceNode()
> > - * \brief Retrieve the video device node path
> > - * \return The video device device node path
> > - */
> > -
> > -std::string V4L2Device::logPrefix() const
> > -{
> > - return deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? "[out]" : "[cap]");
> > -}
> > -
> > -/**
> > - * \brief Retrieve the image format set on the V4L2 device
> > - * \param[out] format The image format applied on the device
> > - * \return 0 on success or a negative error code otherwise
> > - */
> > -int V4L2Device::getFormat(V4L2DeviceFormat *format)
> > -{
> > - if (caps_.isMeta())
> > - return getFormatMeta(format);
> > - else if (caps_.isMultiplanar())
> > - return getFormatMultiplane(format);
> > - else
> > - return getFormatSingleplane(format);
> > -}
> > -
> > -/**
> > - * \brief Configure an image format on the V4L2 device
> > - * \param[inout] format The image format to apply to the device
> > - *
> > - * Apply the supplied \a format to the device, and return the actually
> > - * applied format parameters, as \ref V4L2Device::getFormat would do.
> > - *
> > - * \return 0 on success or a negative error code otherwise
> > - */
> > -int V4L2Device::setFormat(V4L2DeviceFormat *format)
> > -{
> > - if (caps_.isMeta())
> > - return setFormatMeta(format);
> > - else if (caps_.isMultiplanar())
> > - return setFormatMultiplane(format);
> > - else
> > - return setFormatSingleplane(format);
> > -}
> > -
> > -int V4L2Device::getFormatMeta(V4L2DeviceFormat *format)
> > -{
> > - struct v4l2_format v4l2Format = {};
> > - struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
> > - int ret;
> > -
> > - v4l2Format.type = bufferType_;
> > - ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);
> > - if (ret) {
> > - ret = -errno;
> > - LOG(V4L2, Error) << "Unable to get format: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - format->size.width = 0;
> > - format->size.height = 0;
> > - format->fourcc = pix->dataformat;
> > - format->planesCount = 1;
> > - format->planes[0].bpl = pix->buffersize;
> > - format->planes[0].size = pix->buffersize;
> > -
> > - return 0;
> > -}
> > -
> > -int V4L2Device::setFormatMeta(V4L2DeviceFormat *format)
> > -{
> > - struct v4l2_format v4l2Format = {};
> > - struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
> > - int ret;
> > -
> > - v4l2Format.type = bufferType_;
> > - pix->dataformat = format->fourcc;
> > - pix->buffersize = format->planes[0].size;
> > - ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);
> > - if (ret) {
> > - ret = -errno;
> > - LOG(V4L2, Error) << "Unable to set format: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - /*
> > - * Return to caller the format actually applied on the device,
> > - * which might differ from the requested one.
> > - */
> > - format->size.width = 0;
> > - format->size.height = 0;
> > - format->fourcc = format->fourcc;
> > - format->planesCount = 1;
> > - format->planes[0].bpl = pix->buffersize;
> > - format->planes[0].size = pix->buffersize;
> > -
> > - return 0;
> > -}
> >
> > -int V4L2Device::getFormatMultiplane(V4L2DeviceFormat *format)
> > -{
> > - struct v4l2_format v4l2Format = {};
> > - struct v4l2_pix_format_mplane *pix = &v4l2Format.fmt.pix_mp;
> > - int ret;
> > -
> > - v4l2Format.type = bufferType_;
> > - ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);
> > - if (ret) {
> > - ret = -errno;
> > - LOG(V4L2, Error) << "Unable to get format: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - format->size.width = pix->width;
> > - format->size.height = pix->height;
> > - format->fourcc = pix->pixelformat;
> > - format->planesCount = pix->num_planes;
> > -
> > - for (unsigned int i = 0; i < format->planesCount; ++i) {
> > - format->planes[i].bpl = pix->plane_fmt[i].bytesperline;
> > - format->planes[i].size = pix->plane_fmt[i].sizeimage;
> > - }
> > -
> > - return 0;
> > -}
> > -
> > -int V4L2Device::setFormatMultiplane(V4L2DeviceFormat *format)
> > -{
> > - struct v4l2_format v4l2Format = {};
> > - struct v4l2_pix_format_mplane *pix = &v4l2Format.fmt.pix_mp;
> > - int ret;
> > -
> > - v4l2Format.type = bufferType_;
> > - pix->width = format->size.width;
> > - pix->height = format->size.height;
> > - pix->pixelformat = format->fourcc;
> > - pix->num_planes = format->planesCount;
> > - pix->field = V4L2_FIELD_NONE;
> > -
> > - for (unsigned int i = 0; i < pix->num_planes; ++i) {
> > - pix->plane_fmt[i].bytesperline = format->planes[i].bpl;
> > - pix->plane_fmt[i].sizeimage = format->planes[i].size;
> > - }
> > -
> > - ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);
> > - if (ret) {
> > - ret = -errno;
> > - LOG(V4L2, Error) << "Unable to set format: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - /*
> > - * Return to caller the format actually applied on the device,
> > - * which might differ from the requested one.
> > - */
> > - format->size.width = pix->width;
> > - format->size.height = pix->height;
> > - format->fourcc = pix->pixelformat;
> > - format->planesCount = pix->num_planes;
> > - for (unsigned int i = 0; i < format->planesCount; ++i) {
> > - format->planes[i].bpl = pix->plane_fmt[i].bytesperline;
> > - format->planes[i].size = pix->plane_fmt[i].sizeimage;
> > - }
> > -
> > - return 0;
> > -}
> > -
> > -int V4L2Device::getFormatSingleplane(V4L2DeviceFormat *format)
> > -{
> > - struct v4l2_format v4l2Format = {};
> > - struct v4l2_pix_format *pix = &v4l2Format.fmt.pix;
> > - int ret;
> > -
> > - v4l2Format.type = bufferType_;
> > - ret = ioctl(fd_, VIDIOC_G_FMT, &v4l2Format);
> > - if (ret) {
> > - ret = -errno;
> > - LOG(V4L2, Error) << "Unable to get format: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - format->size.width = pix->width;
> > - format->size.height = pix->height;
> > - format->fourcc = pix->pixelformat;
> > - format->planesCount = 1;
> > - format->planes[0].bpl = pix->bytesperline;
> > - format->planes[0].size = pix->sizeimage;
> > -
> > - return 0;
> > -}
> > -
> > -int V4L2Device::setFormatSingleplane(V4L2DeviceFormat *format)
> > -{
> > - struct v4l2_format v4l2Format = {};
> > - struct v4l2_pix_format *pix = &v4l2Format.fmt.pix;
> > - int ret;
> > -
> > - v4l2Format.type = bufferType_;
> > - pix->width = format->size.width;
> > - pix->height = format->size.height;
> > - pix->pixelformat = format->fourcc;
> > - pix->bytesperline = format->planes[0].bpl;
> > - pix->field = V4L2_FIELD_NONE;
> > - ret = ioctl(fd_, VIDIOC_S_FMT, &v4l2Format);
> > - if (ret) {
> > - ret = -errno;
> > - LOG(V4L2, Error) << "Unable to set format: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - /*
> > - * Return to caller the format actually applied on the device,
> > - * which might differ from the requested one.
> > - */
> > - format->size.width = pix->width;
> > - format->size.height = pix->height;
> > - format->fourcc = pix->pixelformat;
> > - format->planesCount = 1;
> > - format->planes[0].bpl = pix->bytesperline;
> > - format->planes[0].size = pix->sizeimage;
> > -
> > - return 0;
> > -}
> > -
> > -int V4L2Device::requestBuffers(unsigned int count)
> > -{
> > - struct v4l2_requestbuffers rb = {};
> > - int ret;
> > -
> > - rb.count = count;
> > - rb.type = bufferType_;
> > - rb.memory = memoryType_;
> > -
> > - ret = ioctl(fd_, VIDIOC_REQBUFS, &rb);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Unable to request " << count << " buffers: "
> > - << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - LOG(V4L2, Debug) << rb.count << " buffers requested.";
> > -
> > - return rb.count;
> > -}
> > -
> > -/**
> > - * \brief Request buffers to be allocated from the device and stored in the
> > - * buffer pool provided.
> > - * \param[out] pool BufferPool to populate with buffers
> > - * \return 0 on success or a negative error code otherwise
> > - */
> > -int V4L2Device::exportBuffers(BufferPool *pool)
> > -{
> > - unsigned int allocatedBuffers;
> > - unsigned int i;
> > - int ret;
> > -
> > - memoryType_ = V4L2_MEMORY_MMAP;
> > -
> > - ret = requestBuffers(pool->count());
> > - if (ret < 0)
> > - return ret;
> > -
> > - allocatedBuffers = ret;
> > - if (allocatedBuffers < pool->count()) {
> > - LOG(V4L2, Error) << "Not enough buffers provided by V4L2Device";
> > - requestBuffers(0);
> > - return -ENOMEM;
> > - }
> > -
> > - /* Map the buffers. */
> > - for (i = 0; i < pool->count(); ++i) {
> > - struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
> > - struct v4l2_buffer buf = {};
> > - Buffer &buffer = pool->buffers()[i];
> > -
> > - buf.index = i;
> > - buf.type = bufferType_;
> > - buf.memory = memoryType_;
> > - buf.length = VIDEO_MAX_PLANES;
> > - buf.m.planes = planes;
> > -
> > - ret = ioctl(fd_, VIDIOC_QUERYBUF, &buf);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Unable to query buffer " << i << ": "
> > - << strerror(-ret);
> > - break;
> > - }
> > -
> > - if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
> > - for (unsigned int p = 0; p < buf.length; ++p) {
> > - ret = createPlane(&buffer, p,
> > - buf.m.planes[p].length);
> > - if (ret)
> > - break;
> > - }
> > - } else {
> > - ret = createPlane(&buffer, 0, buf.length);
> > - }
> > -
> > - if (ret) {
> > - LOG(V4L2, Error) << "Failed to create plane";
> > - break;
> > - }
> > - }
> > -
> > - if (ret) {
> > - requestBuffers(0);
> > - pool->destroyBuffers();
> > - return ret;
> > - }
> > -
> > - bufferPool_ = pool;
> > -
> > - return 0;
> > -}
> > -
> > -int V4L2Device::createPlane(Buffer *buffer, unsigned int planeIndex,
> > - unsigned int length)
> > -{
> > - struct v4l2_exportbuffer expbuf = {};
> > - int ret;
> > -
> > - LOG(V4L2, Debug)
> > - << "Buffer " << buffer->index()
> > - << " plane " << planeIndex
> > - << ": length=" << length;
> > -
> > - expbuf.type = bufferType_;
> > - expbuf.index = buffer->index();
> > - expbuf.plane = planeIndex;
> > - expbuf.flags = O_RDWR;
> > -
> > - ret = ioctl(fd_, VIDIOC_EXPBUF, &expbuf);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to export buffer: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - buffer->planes().emplace_back();
> > - Plane &plane = buffer->planes().back();
> > - plane.setDmabuf(expbuf.fd, length);
> > - ::close(expbuf.fd);
> > -
> > - return 0;
> > -}
> > -
> > -/**
> > - * \brief Import the externally allocated \a pool of buffers
> > - * \param[in] pool BufferPool of buffers to import
> > - * \return 0 on success or a negative error code otherwise
> > - */
> > -int V4L2Device::importBuffers(BufferPool *pool)
> > -{
> > - unsigned int allocatedBuffers;
> > - int ret;
> > -
> > - memoryType_ = V4L2_MEMORY_DMABUF;
> > -
> > - ret = requestBuffers(pool->count());
> > - if (ret < 0)
> > - return ret;
> > -
> > - allocatedBuffers = ret;
> > - if (allocatedBuffers < pool->count()) {
> > - LOG(V4L2, Error)
> > - << "Not enough buffers provided by V4L2Device";
> > - requestBuffers(0);
> > - return -ENOMEM;
> > - }
> > -
> > - LOG(V4L2, Debug) << "provided pool of " << pool->count() << " buffers";
> > - bufferPool_ = pool;
> > + fd_ = ret;
> >
> > return 0;
> > }
> >
> > /**
> > - * \brief Release all internally allocated buffers
> > - */
> > -int V4L2Device::releaseBuffers()
> > -{
> > - LOG(V4L2, Debug) << "Releasing bufferPool";
> > -
> > - bufferPool_ = nullptr;
> > -
> > - return requestBuffers(0);
> > -}
> > -
> > -/**
> > - * \brief Queue a buffer into the device
> > - * \param[in] buffer The buffer to be queued
> > + * \brief Close the device or subdevice
> > *
> > - * For capture devices the \a buffer will be filled with data by the device.
> > - * For output devices the \a buffer shall contain valid data and will be
> > - * processed by the device. Once the device has finished processing the buffer,
> > - * it will be available for dequeue.
> > + * Reset the file descriptor to -1
> > *
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Device::queueBuffer(Buffer *buffer)
> > +int V4L2Device::close()
> > {
> > - struct v4l2_buffer buf = {};
> > - struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
> > - int ret;
> > -
> > - buf.index = buffer->index();
> > - buf.type = bufferType_;
> > - buf.memory = memoryType_;
> > - buf.field = V4L2_FIELD_NONE;
> > -
> > - bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type);
> > -
> > - if (buf.memory == V4L2_MEMORY_DMABUF) {
> > - if (multiPlanar) {
> > - for (unsigned int p = 0;
> > - p < buffer->planes().size();
> > - p++)
> > - planes[p].m.fd = buffer->planes()[p].dmabuf();
> > - } else {
> > - buf.m.fd = buffer->planes()[0].dmabuf();
> > - }
> > - }
> > -
> > - if (multiPlanar) {
> > - buf.length = buffer->planes().size();
> > - buf.m.planes = planes;
> > - }
> > -
> > - if (V4L2_TYPE_IS_OUTPUT(bufferType_)) {
> > - buf.bytesused = buffer->bytesused_;
> > - buf.sequence = buffer->sequence_;
> > - buf.timestamp.tv_sec = buffer->timestamp_ / 1000000000;
> > - buf.timestamp.tv_usec = (buffer->timestamp_ / 1000) % 1000000;
> > - }
> > -
> > - LOG(V4L2, Debug) << "Queueing buffer " << buf.index;
> > + if (fd_ < 0)
> > + return 0;
> >
> > - ret = ioctl(fd_, VIDIOC_QBUF, &buf);
> > + int ret = ::close(fd_);
> > if (ret < 0) {
> > ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to queue buffer " << buf.index << ": "
> > - << strerror(-ret);
> > + LOG(V4L2, Error) << "Failed to close V4L2 device: "
> > + << strerror(-ret);
> > return ret;
> > }
> >
> > - if (queuedBuffersCount_++ == 0)
> > - fdEvent_->setEnabled(true);
> > + fd_ = -1;
> >
> > return 0;
> > }
> >
> > /**
> > - * \brief Dequeue the next available buffer from the device
> > - *
> > - * This method dequeues the next available buffer from the device. If no buffer
> > - * is available to be dequeued it will return nullptr immediately.
> > - *
> > - * \return A pointer to the dequeued buffer on success, or nullptr otherwise
> > - */
> > -Buffer *V4L2Device::dequeueBuffer()
> > -{
> > - struct v4l2_buffer buf = {};
> > - struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
> > - int ret;
> > -
> > - buf.type = bufferType_;
> > - buf.memory = memoryType_;
> > -
> > - if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
> > - buf.length = VIDEO_MAX_PLANES;
> > - buf.m.planes = planes;
> > - }
> > -
> > - ret = ioctl(fd_, VIDIOC_DQBUF, &buf);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to dequeue buffer: " << strerror(-ret);
> > - return nullptr;
> > - }
> > -
> > - ASSERT(buf.index < bufferPool_->count());
> > -
> > - if (--queuedBuffersCount_ == 0)
> > - fdEvent_->setEnabled(false);
> > -
> > - Buffer *buffer = &bufferPool_->buffers()[buf.index];
> > -
> > - buffer->bytesused_ = buf.bytesused;
> > - buffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL
> > - + buf.timestamp.tv_usec * 1000ULL;
> > - buffer->sequence_ = buf.sequence;
> > - buffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR
> > - ? Buffer::BufferError : Buffer::BufferSuccess;
> > -
> > - return buffer;
> > -}
> > -
> > -/**
> > - * \brief Slot to handle completed buffer events from the V4L2 device
> > - * \param[in] notifier The event notifier
> > - *
> > - * When this slot is called, a Buffer has become available from the device, and
> > - * will be emitted through the bufferReady Signal.
> > - *
> > - * For Capture devices the Buffer will contain valid data.
> > - * For Output devices the Buffer can be considered empty.
> > + * \brief Check if the V4L2 device or subdevice is open
> > + * \return True if the V4L2 device or subdevice is open, false otherwise
> > */
> > -void V4L2Device::bufferAvailable(EventNotifier *notifier)
> > -{
> > - Buffer *buffer = dequeueBuffer();
> > - if (!buffer)
> > - return;
> > -
> > - LOG(V4L2, Debug) << "Buffer " << buffer->index() << " is available";
> > -
> > - /* Notify anyone listening to the device. */
> > - bufferReady.emit(buffer);
> > -}
> > -
> > -/**
> > - * \var V4L2Device::bufferReady
> > - * \brief A Signal emitted when a buffer completes
> > - */
> > -
> > -/**
> > - * \brief Start the video stream
> > - * \return 0 on success or a negative error code otherwise
> > - */
> > -int V4L2Device::streamOn()
> > +bool V4L2Device::isOpen() const
> > {
> > - int ret;
> > -
> > - ret = ioctl(fd_, VIDIOC_STREAMON, &bufferType_);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to start streaming: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - return 0;
> > + return fd_ != -1;
> > }
> >
> > /**
> > - * \brief Stop the video stream
> > - *
> > - * Buffers that are still queued when the video stream is stopped are
> > - * implicitly dequeued, but no bufferReady signal is emitted for them.
> > - *
> > + * \brief Perform an IOCTL system call on the device node
> > + * \param request The IOCTL request code
> > + * \param argp A poiunter to the IOCTL argument
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Device::streamOff()
> > +int V4L2Device::ioctl(unsigned long request, void *argp)
> > {
> > - int ret;
> > -
> > - ret = ioctl(fd_, VIDIOC_STREAMOFF, &bufferType_);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2, Error)
> > - << "Failed to stop streaming: " << strerror(-ret);
> > - return ret;
> > - }
> > -
> > - queuedBuffersCount_ = 0;
> > - fdEvent_->setEnabled(false);
> > + /*
> > + * Printing out an error message is usually better performed
> > + * in the caller, which can provide more context.
> > + */
> > + if (::ioctl(fd_, request, argp) < 0)
> > + return -errno;
> >
> > return 0;
> > }
> >
> > /**
> > - * \brief Create a new video device instance from \a entity in media device
> > - * \a media
> > - * \param[in] media The media device where the entity is registered
> > - * \param[in] entity The media entity name
> > - *
> > - * Releasing memory of the newly created instance is responsibility of the
> > - * caller of this function.
> > + * \var V4L2Device::fd_
> > + * \brief The V4L2 device or subdevice device node file descriptor
> > *
> > - * \return A newly created V4L2Device on success, nullptr otherwise
> > + * The file descriptor is initialized to -1 and reset to this value once
> > + * the device or subdevice gets closed.
> > */
> > -V4L2Device *V4L2Device::fromEntityName(const MediaDevice *media,
> > - const std::string &entity)
> > -{
> > - MediaEntity *mediaEntity = media->getEntityByName(entity);
> > - if (!mediaEntity)
> > - return nullptr;
> > -
> > - return new V4L2Device(mediaEntity);
> > -}
> >
> > -} /* namespace libcamera */
> > +}; /* namespace libcamera */
>
> ^ Possibly shouldn't be a semi-colon there?
>
> > diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdev.cpp
> > similarity index 79%
> > rename from src/libcamera/v4l2_subdevice.cpp
> > rename to src/libcamera/v4l2_subdev.cpp
> > index fceee33156e9..60bbd07284ff 100644
> > --- a/src/libcamera/v4l2_subdevice.cpp
> > +++ b/src/libcamera/v4l2_subdev.cpp
> > @@ -2,10 +2,10 @@
> > /*
> > * Copyright (C) 2019, Google Inc.
> > *
> > - * v4l2_subdevice.cpp - V4L2 Subdevice
> > + * v4l2_subdev.cpp - V4L2 Subdevice
> > */
> >
> > -#include "v4l2_subdevice.h"
> > +#include "v4l2_subdev.h"
> >
> > #include <fcntl.h>
> > #include <iomanip>
> > @@ -23,7 +23,7 @@
> > #include "media_object.h"
> >
> > /**
> > - * \file v4l2_subdevice.h
> > + * \file v4l2_subdev.h
> > * \brief V4L2 Subdevice API
> > */
> >
> > @@ -44,7 +44,7 @@ LOG_DEFINE_CATEGORY(V4L2Subdev)
> > * as the "media bus format", and it is identified by a resolution and a pixel
> > * format identification code, known as the "media bus code", not to be confused
> > * with the fourcc code that identify the format of images when stored in memory
> > - * (see V4L2Device::V4L2DeviceFormat).
> > + * (see V4L2Videodev::V4L2DeviceFormat).
> > *
> > * Media Bus formats supported by the V4L2 APIs are described in Section
> > * 4.15.3.4.1 of the "Part I - Video for Linux API" chapter of the "Linux Media
> > @@ -84,14 +84,14 @@ const std::string V4L2SubdeviceFormat::toString() const
> > }
> >
> > /**
> > - * \class V4L2Subdevice
> > + * \class V4L2Subdev
> > * \brief A V4L2 subdevice as exposed by the Linux kernel
> > *
> > - * The V4L2Subdevice class provides an API to the "Sub-device interface" as
> > + * The V4L2Subdev class provides an API to the "Sub-device interface" as
> > * described in section 4.15 of the "Linux Media Infrastructure userspace API"
> > * chapter of the Linux Kernel documentation.
> > *
> > - * A V4L2Subdevice is constructed from a MediaEntity instance, using the system
> > + * A V4L2Subdev is constructed from a MediaEntity instance, using the system
> > * path of the entity's device node. No API call other than open(), isOpen()
> > * and close() shall be called on an unopened device instance. Upon destruction
> > * any device left open will be closed, and any resources released.
> > @@ -101,65 +101,39 @@ const std::string V4L2SubdeviceFormat::toString() const
> > * \brief Create a V4L2 subdevice from a MediaEntity using its device node
> > * path
> > */
> > -V4L2Subdevice::V4L2Subdevice(const MediaEntity *entity)
> > - : entity_(entity), fd_(-1)
> > +V4L2Subdev::V4L2Subdev(const MediaEntity *entity)
> > + : V4L2Device(), entity_(entity)
> > {
> > }
> >
> > -V4L2Subdevice::~V4L2Subdevice()
> > +V4L2Subdev::~V4L2Subdev()
> > {
> > - close();
> > + V4L2Device::close();
> > }
> >
> > /**
> > * \brief Open a V4L2 subdevice
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Subdevice::open()
> > +int V4L2Subdev::open()
> > {
> > - int ret;
> > -
> > - if (isOpen()) {
> > - LOG(V4L2Subdev, Error) << "Device already open";
> > - return -EBUSY;
> > - }
> > -
> > - ret = ::open(entity_->deviceNode().c_str(), O_RDWR);
> > - if (ret < 0) {
> > - ret = -errno;
> > - LOG(V4L2Subdev, Error)
> > - << "Failed to open V4L2 subdevice '"
> > - << entity_->deviceNode() << "': " << strerror(-ret);
> > + int ret = V4L2Device::open(entity_->deviceNode(), O_RDWR);
> > + if (ret)
> > return ret;
> > - }
> > - fd_ = ret;
> >
> > return 0;
> > }
> >
> > -/**
> > - * \brief Check if the subdevice is open
> > - * \return True if the subdevice is open, false otherwise
> > - */
> > -bool V4L2Subdevice::isOpen() const
> > -{
> > - return fd_ != -1;
> > -}
> > -
> > /**
> > * \brief Close the subdevice, releasing any resources acquired by open()
> > */
> > -void V4L2Subdevice::close()
> > +void V4L2Subdev::close()
> > {
> > - if (!isOpen())
> > - return;
> > -
> > - ::close(fd_);
> > - fd_ = -1;
> > + V4L2Device::close();
> > }
> >
> > /**
> > - * \fn V4L2Subdevice::entity()
> > + * \fn V4L2Subdev::entity()
> > * \brief Retrieve the media entity associated with the subdevice
> > * \return The subdevice's associated media entity.
> > */
> > @@ -170,7 +144,7 @@ void V4L2Subdevice::close()
> > * \param[inout] rect The rectangle describing crop target area
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect)
> > +int V4L2Subdev::setCrop(unsigned int pad, Rectangle *rect)
> > {
> > return setSelection(pad, V4L2_SEL_TGT_CROP, rect);
> > }
> > @@ -181,7 +155,7 @@ int V4L2Subdevice::setCrop(unsigned int pad, Rectangle *rect)
> > * \param[inout] rect The rectangle describing the compose target area
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect)
> > +int V4L2Subdev::setCompose(unsigned int pad, Rectangle *rect)
> > {
> > return setSelection(pad, V4L2_SEL_TGT_COMPOSE, rect);
> > }
> > @@ -200,7 +174,7 @@ int V4L2Subdevice::setCompose(unsigned int pad, Rectangle *rect)
> > * \return A map of image formats associated with a list of image sizes, or
> > * an empty map on error or if the pad does not exist
> > */
> > -FormatEnum V4L2Subdevice::formats(unsigned int pad)
> > +FormatEnum V4L2Subdev::formats(unsigned int pad)
> > {
> > FormatEnum formatMap = {};
> > struct v4l2_subdev_mbus_code_enum mbusEnum = {};
> > @@ -215,7 +189,7 @@ FormatEnum V4L2Subdevice::formats(unsigned int pad)
> > mbusEnum.index = 0;
> > mbusEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > while (true) {
> > - ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);
> > + ret = V4L2Device::ioctl(VIDIOC_SUBDEV_ENUM_MBUS_CODE, &mbusEnum);
>
> I think the V4L2Device:: prefix is unecessary here, as it will match
> against the base class fucntion protoype. However I'm not opposed to
> being explicit to make it clear that this isn't directly calling ::ioctl()
>
>
> > if (ret)
> > break;
> >
> > @@ -244,13 +218,13 @@ FormatEnum V4L2Subdevice::formats(unsigned int pad)
> > * \param[out] format The image bus format
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > +int V4L2Subdev::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > {
> > struct v4l2_subdev_format subdevFmt = {};
> > subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > subdevFmt.pad = pad;
> >
> > - int ret = ioctl(fd_, VIDIOC_SUBDEV_G_FMT, &subdevFmt);
> > + int ret = V4L2Device::ioctl(VIDIOC_SUBDEV_G_FMT, &subdevFmt);
> > if (ret) {
> > ret = -errno;
> > LOG(V4L2Subdev, Error)
> > @@ -272,12 +246,12 @@ int V4L2Subdevice::getFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > * \param[inout] format The image bus format to apply to the subdevice's pad
> > *
> > * Apply the requested image format to the desired media pad and return the
> > - * actually applied format parameters, as \ref V4L2Subdevice::getFormat would
> > + * actually applied format parameters, as \ref V4L2Subdev::getFormat would
> > * do.
> > *
> > * \return 0 on success or a negative error code otherwise
> > */
> > -int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > +int V4L2Subdev::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > {
> > struct v4l2_subdev_format subdevFmt = {};
> > subdevFmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > @@ -286,7 +260,7 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > subdevFmt.format.height = format->size.height;
> > subdevFmt.format.code = format->mbus_code;
> >
> > - int ret = ioctl(fd_, VIDIOC_SUBDEV_S_FMT, &subdevFmt);
> > + int ret = V4L2Device::ioctl(VIDIOC_SUBDEV_S_FMT, &subdevFmt);
> > if (ret) {
> > ret = -errno;
> > LOG(V4L2Subdev, Error)
> > @@ -311,25 +285,25 @@ int V4L2Subdevice::setFormat(unsigned int pad, V4L2SubdeviceFormat *format)
> > * Releasing memory of the newly created instance is responsibility of the
> > * caller of this function.
> > *
> > - * \return A newly created V4L2Subdevice on success, nullptr otherwise
> > + * \return A newly created V4L2Subdev on success, nullptr otherwise
> > */
> > -V4L2Subdevice *V4L2Subdevice::fromEntityName(const MediaDevice *media,
> > - const std::string &entity)
> > +V4L2Subdev *V4L2Subdev::fromEntityName(const MediaDevice *media,
> > + const std::string &entity)
> > {
> > MediaEntity *mediaEntity = media->getEntityByName(entity);
> > if (!mediaEntity)
> > return nullptr;
> >
> > - return new V4L2Subdevice(mediaEntity);
> > + return new V4L2Subdev(mediaEntity);
> > }
> >
> > -std::string V4L2Subdevice::logPrefix() const
> > +std::string V4L2Subdev::logPrefix() const
> > {
> > return "'" + entity_->name() + "'";
> > }
> >
> > -int V4L2Subdevice::enumPadSizes(unsigned int pad,unsigned int code,
> > - std::vector<SizeRange> *sizes)
> > +int V4L2Subdev::enumPadSizes(unsigned int pad,unsigned int code,
> > + std::vector<SizeRange> *sizes)
>
>
> alignment issue. ?
> Actually - maybe this is just my mail client mis representing the tabs
> and indents... so perhaps this and the others are fine.
>
>
>
> > {
> > struct v4l2_subdev_frame_size_enum sizeEnum = {};
> > int ret;
> > @@ -339,7 +313,7 @@ int V4L2Subdevice::enumPadSizes(unsigned int pad,unsigned int code,
> > sizeEnum.code = code;
> > sizeEnum.which = V4L2_SUBDEV_FORMAT_ACTIVE;
> > while (true) {
> > - ret = ioctl(fd_, VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);
> > + ret = V4L2Device::ioctl(VIDIOC_SUBDEV_ENUM_FRAME_SIZE, &sizeEnum);
> > if (ret)
> > break;
> >
> > @@ -362,8 +336,8 @@ int V4L2Subdevice::enumPadSizes(unsigned int pad,unsigned int code,
> > return 0;
> > }
> >
> > -int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
> > - Rectangle *rect)
> > +int V4L2Subdev::setSelection(unsigned int pad, unsigned int target,
> > + Rectangle *rect)
>
> Yes, in fact I see the same indent on the before and after relative to
> the '(' so it must be just me :)
>
>
> > {
> > struct v4l2_subdev_selection sel = {};
> >
> > @@ -377,7 +351,7 @@ int V4L2Subdevice::setSelection(unsigned int pad, unsigned int target,
> > sel.r.width = rect->w;
> > sel.r.height = rect->h;
> >
> > - int ret = ioctl(fd_, VIDIOC_SUBDEV_S_SELECTION, &sel);
> > + int ret = V4L2Device::ioctl(VIDIOC_SUBDEV_S_SELECTION, &sel);
> > if (ret < 0) {
> > ret = -errno;
> > LOG(V4L2Subdev, Error)
> > diff --git a/src/libcamera/v4l2_videodev.cpp b/src/libcamera/v4l2_videodev.cpp
> > new file mode 100644
> > index 000000000000..6ad3af53a3bd
> > --- /dev/null
> > +++ b/src/libcamera/v4l2_videodev.cpp
> > @@ -0,0 +1,992 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2019, Google Inc.
> > + *
> > + * v4l2_videodev.cpp - V4L2 Video Device
> > + */
> > +
> > +#include "v4l2_videodev.h"
> > +
> > +#include <fcntl.h>
> > +#include <iomanip>
> > +#include <sstream>
> > +#include <string.h>
> > +#include <sys/ioctl.h>
> > +#include <sys/mman.h>
> > +#include <sys/time.h>
> > +#include <unistd.h>
> > +#include <vector>
> > +
> > +#include <libcamera/buffer.h>
> > +#include <libcamera/event_notifier.h>
> > +
> > +#include "log.h"
> > +#include "media_device.h"
> > +#include "media_object.h"
> > +
> > +/**
> > + * \file v4l2_videodev.h
> > + * \brief V4L2 Video Device API
> > + */
> > +namespace libcamera {
> > +
> > +LOG_DECLARE_CATEGORY(V4L2)
> > +
> > +/**
> > + * \struct V4L2Capability
> > + * \brief struct v4l2_capability object wrapper and helpers
> > + *
> > + * The V4L2Capability structure manages the information returned by the
> > + * VIDIOC_QUERYCAP ioctl.
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::driver()
> > + * \brief Retrieve the driver module name
> > + * \return The string containing the name of the driver module
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::card()
> > + * \brief Retrieve the device card name
> > + * \return The string containing the device name
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::bus_info()
> > + * \brief Retrieve the location of the device in the system
> > + * \return The string containing the device location
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::device_caps()
> > + * \brief Retrieve the capabilities of the device
> > + * \return The device specific capabilities if V4L2_CAP_DEVICE_CAPS is set or
> > + * driver capabilities otherwise
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isMultiplanar()
> > + * \brief Identify if the device implements the V4L2 multiplanar APIs
> > + * \return True if the device supports multiplanar APIs
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isCapture()
> > + * \brief Identify if the device captures data
> > + * \return True if the device can capture data
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isOutput()
> > + * \brief Identify if the device outputs data
> > + * \return True if the device can output data
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isVideo()
> > + * \brief Identify if the device captures or outputs images
> > + * \return True if the device can capture or output images
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isMeta()
> > + * \brief Identify if the device captures or outputs image meta-data
> > + *
> > + * \todo Add support for META_CAPTURE introduced in Linux v5.0
> > + *
> > + * \return True if the device can capture or output image meta-data
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isVideoCapture()
> > + * \brief Identify if the device captures images
> > + * \return True if the device can capture images
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isVideoOutput()
> > + * \brief Identify if the device outputs images
> > + * \return True if the device can output images
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isMetaCapture()
> > + * \brief Identify if the device captures image meta-data
> > + * \return True if the device can capture image meta-data
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::isMetaOutput()
> > + * \brief Identify if the device outputs image meta-data
> > + * \return True if the device can output image meta-data
> > + */
> > +
> > +/**
> > + * \fn V4L2Capability::hasStreaming()
> > + * \brief Determine if the device can perform Streaming I/O
> > + * \return True if the device provides Streaming I/O IOCTLs
> > + */
> > +
> > +/**
> > + * \class V4L2DeviceFormat
> > + * \brief The V4L2 device image format and sizes
> > + *
> > + * This class describes the image format and resolution to be programmed on a
> > + * V4L2 video device. The image format is defined by a fourcc code (as specified
> > + * by the V4L2 API with the V4L2_PIX_FMT_* macros), a resolution (width and
> > + * height) and one to three planes with configurable line stride and a total
> > + * per-plane size in bytes.
> > + *
> > + * Image formats, as defined by the V4L2 APIs, are categorised as packed,
> > + * semi-planar and planar, and describe the layout of the image pixel components
> > + * stored in memory.
> > + *
> > + * Packed image formats store pixel components one after the other, in a
> > + * contiguous memory area. Examples of packed image formats are YUYV
> > + * permutations, RGB with different pixel sub-sampling ratios such as RGB565 or
> > + * RGB666 or Raw-Bayer formats such as SRGGB8 or SGRBG12.
> > + *
> > + * Semi-planar and planar image formats store the pixel components in separate
> > + * and possibly non-contiguous memory areas, named planes, whose sizes depend on
> > + * the pixel components sub-sampling ratios, which are defined by the format.
> > + * Semi-planar formats use two planes to store pixel components and notable
> > + * examples of such formats are the NV12 and NV16 formats, while planar formats
> > + * use three planes to store pixel components and notable examples are YUV422
> > + * and YUV420.
> > + *
> > + * Image formats supported by the V4L2 API are defined and described in Section
> > + * number 2 of the "Part I - Video for Linux API" chapter of the "Linux Media
> > + * Infrastructure userspace API", part of the Linux kernel documentation.
> > + *
> > + * In the context of this document, packed image formats are referred to as
> > + * "packed formats" and semi-planar and planar image formats are referred to as
> > + * "planar formats".
> > + *
> > + * V4L2 also defines two different sets of APIs to work with devices that store
> > + * planes in contiguous or separate memory areas. They are named "Single-plane
> > + * APIs" and "Multi-plane APIs" respectively and are documented in Section 2.1
> > + * and Section 2.2 of the above mentioned "Part I - Video for Linux API"
> > + * documentation.
> > + *
> > + * The single-plane API allows, among other parameters, the configuration of the
> > + * image resolution, the pixel format and the stride length. In that case the
> > + * stride applies to all planes (possibly sub-sampled). The multi-plane API
> > + * allows configuring the resolution, the pixel format and a per-plane stride
> > + * length and total size.
> > + *
> > + * Packed image formats, which occupy a single memory area, are easily described
> > + * through the single-plane API. When used on a device that implements the
> > + * multi-plane API, only the size and stride information contained in the first
> > + * plane are taken into account.
> > + *
> > + * Planar image formats, which occupy distinct memory areas, are easily
> > + * described through the multi-plane APIs. When used on a device that implements
> > + * the single-plane API, all planes are stored one after the other in a
> > + * contiguous memory area, and it is not possible to configure per-plane stride
> > + * length and size, but only a global stride length which is applied to all
> > + * planes.
> > + *
> > + * The V4L2DeviceFormat class describes both packed and planar image formats,
> > + * regardless of the API type (single or multi plane) implemented by the device
> > + * the format has to be applied to. The total size and bytes per line of images
> > + * represented with packed formats are configured using the first entry of the
> > + * V4L2DeviceFormat::planes array, while the per-plane size and per-plane stride
> > + * length of images represented with planar image formats are configured using
> > + * the opportune number of entries of the V4L2DeviceFormat::planes array, as
> > + * prescribed by the image format definition (semi-planar formats use 2 entries,
> > + * while planar formats use the whole 3 entries). The number of valid entries of
> > + * the V4L2DeviceFormat::planes array is defined by the
> > + * V4L2DeviceFormat::planesCount value.
> > + */
> > +
> > +/**
> > + * \var V4L2DeviceFormat::size
> > + * \brief The image size in pixels
> > + */
> > +
> > +/**
> > + * \var V4L2DeviceFormat::fourcc
> > + * \brief The fourcc code describing the pixel encoding scheme
> > + *
> > + * The fourcc code, as defined by the V4L2 API with the V4L2_PIX_FMT_* macros,
> > + * that identifies the image format pixel encoding scheme.
> > + */
> > +
> > +/**
> > + * \var V4L2DeviceFormat::planes
> > + * \brief The per-plane memory size information
> > + *
> > + * Images are stored in memory in one or more data planes. Each data plane has a
> > + * specific line stride and memory size, which could differ from the image
> > + * visible sizes to accommodate padding at the end of lines and end of planes.
> > + * Only the first \ref planesCount entries are considered valid.
> > + */
> > +
> > +/**
> > + * \var V4L2DeviceFormat::planesCount
> > + * \brief The number of valid data planes
> > + */
> > +
> > +/**
> > + * \brief Assemble and return a string describing the format
> > + * \return A string describing the V4L2DeviceFormat
> > + */
> > +const std::string V4L2DeviceFormat::toString() const
> > +{
> > + std::stringstream ss;
> > +
> > + ss.fill(0);
> > + ss << size.toString() << "-0x" << std::hex << std::setw(8) << fourcc;
> > +
> > + return ss.str();
> > +}
> > +
> > +/**
> > + * \class V4L2Videodev
> > + * \brief V4L2Videodev object and API
> > + *
> > + * The V4L2 Device API class models an instance of a V4L2 device node.
> > + * It is constructed with the path to a V4L2 video device node. The device node
> > + * is only opened upon a call to open() which must be checked for success.
> > + *
> > + * The device capabilities are validated when the device is opened and the
> > + * device is rejected if it is not a suitable V4L2 capture or output device, or
> > + * if the device does not support streaming I/O.
> > + *
> > + * No API call other than open(), isOpen() and close() shall be called on an
> > + * unopened device instance.
> > + *
> > + * The V4L2Videodev class tracks queued buffers and handles buffer events. It
> > + * automatically dequeues completed buffers and emits the \ref bufferReady
> > + * signal.
> > + *
> > + * Upon destruction any device left open will be closed, and any resources
> > + * released.
> > + */
> > +
> > +/**
> > + * \brief Construct a V4L2Videodev
> > + * \param[in] deviceNode The file-system path to the video device node
> > + */
> > +V4L2Videodev::V4L2Videodev(const std::string &deviceNode)
> > + : V4L2Device(), deviceNode_(deviceNode), bufferPool_(nullptr),
> > + queuedBuffersCount_(0), fdEvent_(nullptr)
> > +{
> > + /*
> > + * We default to an MMAP based CAPTURE device, however this will be
> > + * updated based upon the device capabilities.
> > + */
> > + bufferType_ = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
> > + memoryType_ = V4L2_MEMORY_MMAP;
> > +}
> > +
> > +/**
> > + * \brief Construct a V4L2Videodev from a MediaEntity
> > + * \param[in] entity The MediaEntity to build the device from
> > + *
> > + * Construct a V4L2Videodev from a MediaEntity's device node path.
> > + */
> > +V4L2Videodev::V4L2Videodev(const MediaEntity *entity)
> > + : V4L2Videodev(entity->deviceNode())
> > +{
> > +}
> > +
> > +V4L2Videodev::~V4L2Videodev()
> > +{
> > + V4L2Device::close();
> > +}
> > +
> > +/**
> > + * \brief Open a V4L2 device and query its capabilities
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::open()
> > +{
> > + int ret;
> > +
> > + ret = V4L2Device::open(deviceNode_, O_RDWR | O_NONBLOCK);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_QUERYCAP, &caps_);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Failed to query device capabilities: "
> > + << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + LOG(V4L2, Debug)
> > + << "Opened device " << caps_.bus_info() << ": "
> > + << caps_.driver() << ": " << caps_.card();
> > +
> > + 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 devices
> > + * (POLLIN), and write notifications for OUTPUT 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;
> > + }
> > +
> > + fdEvent_->activated.connect(this, &V4L2Videodev::bufferAvailable);
> > + fdEvent_->setEnabled(false);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * \brief Close the device, releasing any resources acquired by open()
> > + */
> > +void V4L2Videodev::close()
> > +{
> > + if (fd() < 0)
> > + return;
> > +
> > + releaseBuffers();
> > + delete fdEvent_;
> > +
> > + V4L2Device::close();
> > +}
> > +
> > +/**
> > + * \fn V4L2Videodev::driverName()
> > + * \brief Retrieve the name of the V4L2 device driver
> > + * \return The string containing the driver name
> > + */
> > +
> > +/**
> > + * \fn V4L2Videodev::deviceName()
> > + * \brief Retrieve the name of the V4L2 device
> > + * \return The string containing the device name
> > + */
> > +
> > +/**
> > + * \fn V4L2Videodev::busName()
> > + * \brief Retrieve the location of the device in the system
> > + * \return The string containing the device location
> > + */
> > +
> > +/**
> > + * \fn V4L2Videodev::deviceNode()
> > + * \brief Retrieve the video device node path
> > + * \return The video device device node path
> > + */
> > +
> > +std::string V4L2Videodev::logPrefix() const
> > +{
> > + return deviceNode_ + (V4L2_TYPE_IS_OUTPUT(bufferType_) ? "[out]" : "[cap]");
> > +}
> > +
> > +/**
> > + * \brief Retrieve the image format set on the V4L2 device
> > + * \param[out] format The image format applied on the device
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::getFormat(V4L2DeviceFormat *format)
> > +{
> > + if (caps_.isMeta())
> > + return getFormatMeta(format);
> > + else if (caps_.isMultiplanar())
> > + return getFormatMultiplane(format);
> > + else
> > + return getFormatSingleplane(format);
> > +}
> > +
> > +/**
> > + * \brief Configure an image format on the V4L2 device
> > + * \param[inout] format The image format to apply to the device
> > + *
> > + * Apply the supplied \a format to the device, and return the actually
> > + * applied format parameters, as \ref V4L2Videodev::getFormat would do.
> > + *
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::setFormat(V4L2DeviceFormat *format)
> > +{
> > + if (caps_.isMeta())
> > + return setFormatMeta(format);
> > + else if (caps_.isMultiplanar())
> > + return setFormatMultiplane(format);
> > + else
> > + return setFormatSingleplane(format);
> > +}
> > +
> > +int V4L2Videodev::getFormatMeta(V4L2DeviceFormat *format)
> > +{
> > + struct v4l2_format v4l2Format = {};
> > + struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
> > + int ret;
> > +
> > + v4l2Format.type = bufferType_;
> > + ret = V4L2Device::ioctl(VIDIOC_G_FMT, &v4l2Format);
> > + if (ret) {
> > + ret = -errno;
> > + LOG(V4L2, Error) << "Unable to get format: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + format->size.width = 0;
> > + format->size.height = 0;
> > + format->fourcc = pix->dataformat;
> > + format->planesCount = 1;
> > + format->planes[0].bpl = pix->buffersize;
> > + format->planes[0].size = pix->buffersize;
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::setFormatMeta(V4L2DeviceFormat *format)
> > +{
> > + struct v4l2_format v4l2Format = {};
> > + struct v4l2_meta_format *pix = &v4l2Format.fmt.meta;
> > + int ret;
> > +
> > + v4l2Format.type = bufferType_;
> > + pix->dataformat = format->fourcc;
> > + pix->buffersize = format->planes[0].size;
> > + ret = V4L2Device::ioctl(VIDIOC_S_FMT, &v4l2Format);
> > + if (ret) {
> > + ret = -errno;
> > + LOG(V4L2, Error) << "Unable to set format: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + /*
> > + * Return to caller the format actually applied on the device,
> > + * which might differ from the requested one.
> > + */
> > + format->size.width = 0;
> > + format->size.height = 0;
> > + format->fourcc = format->fourcc;
> > + format->planesCount = 1;
> > + format->planes[0].bpl = pix->buffersize;
> > + format->planes[0].size = pix->buffersize;
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::getFormatMultiplane(V4L2DeviceFormat *format)
> > +{
> > + struct v4l2_format v4l2Format = {};
> > + struct v4l2_pix_format_mplane *pix = &v4l2Format.fmt.pix_mp;
> > + int ret;
> > +
> > + v4l2Format.type = bufferType_;
> > + ret = V4L2Device::ioctl(VIDIOC_G_FMT, &v4l2Format);
> > + if (ret) {
> > + ret = -errno;
> > + LOG(V4L2, Error) << "Unable to get format: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + format->size.width = pix->width;
> > + format->size.height = pix->height;
> > + format->fourcc = pix->pixelformat;
> > + format->planesCount = pix->num_planes;
> > +
> > + for (unsigned int i = 0; i < format->planesCount; ++i) {
> > + format->planes[i].bpl = pix->plane_fmt[i].bytesperline;
> > + format->planes[i].size = pix->plane_fmt[i].sizeimage;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::setFormatMultiplane(V4L2DeviceFormat *format)
> > +{
> > + struct v4l2_format v4l2Format = {};
> > + struct v4l2_pix_format_mplane *pix = &v4l2Format.fmt.pix_mp;
> > + int ret;
> > +
> > + v4l2Format.type = bufferType_;
> > + pix->width = format->size.width;
> > + pix->height = format->size.height;
> > + pix->pixelformat = format->fourcc;
> > + pix->num_planes = format->planesCount;
> > + pix->field = V4L2_FIELD_NONE;
> > +
> > + for (unsigned int i = 0; i < pix->num_planes; ++i) {
> > + pix->plane_fmt[i].bytesperline = format->planes[i].bpl;
> > + pix->plane_fmt[i].sizeimage = format->planes[i].size;
> > + }
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_S_FMT, &v4l2Format);
> > + if (ret) {
> > + ret = -errno;
> > + LOG(V4L2, Error) << "Unable to set format: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + /*
> > + * Return to caller the format actually applied on the device,
> > + * which might differ from the requested one.
> > + */
> > + format->size.width = pix->width;
> > + format->size.height = pix->height;
> > + format->fourcc = pix->pixelformat;
> > + format->planesCount = pix->num_planes;
> > + for (unsigned int i = 0; i < format->planesCount; ++i) {
> > + format->planes[i].bpl = pix->plane_fmt[i].bytesperline;
> > + format->planes[i].size = pix->plane_fmt[i].sizeimage;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::getFormatSingleplane(V4L2DeviceFormat *format)
> > +{
> > + struct v4l2_format v4l2Format = {};
> > + struct v4l2_pix_format *pix = &v4l2Format.fmt.pix;
> > + int ret;
> > +
> > + v4l2Format.type = bufferType_;
> > + ret = V4L2Device::ioctl(VIDIOC_G_FMT, &v4l2Format);
> > + if (ret) {
> > + ret = -errno;
> > + LOG(V4L2, Error) << "Unable to get format: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + format->size.width = pix->width;
> > + format->size.height = pix->height;
> > + format->fourcc = pix->pixelformat;
> > + format->planesCount = 1;
> > + format->planes[0].bpl = pix->bytesperline;
> > + format->planes[0].size = pix->sizeimage;
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::setFormatSingleplane(V4L2DeviceFormat *format)
> > +{
> > + struct v4l2_format v4l2Format = {};
> > + struct v4l2_pix_format *pix = &v4l2Format.fmt.pix;
> > + int ret;
> > +
> > + v4l2Format.type = bufferType_;
> > + pix->width = format->size.width;
> > + pix->height = format->size.height;
> > + pix->pixelformat = format->fourcc;
> > + pix->bytesperline = format->planes[0].bpl;
> > + pix->field = V4L2_FIELD_NONE;
> > + ret = V4L2Device::ioctl(VIDIOC_S_FMT, &v4l2Format);
> > + if (ret) {
> > + ret = -errno;
> > + LOG(V4L2, Error) << "Unable to set format: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + /*
> > + * Return to caller the format actually applied on the device,
> > + * which might differ from the requested one.
> > + */
> > + format->size.width = pix->width;
> > + format->size.height = pix->height;
> > + format->fourcc = pix->pixelformat;
> > + format->planesCount = 1;
> > + format->planes[0].bpl = pix->bytesperline;
> > + format->planes[0].size = pix->sizeimage;
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::requestBuffers(unsigned int count)
> > +{
> > + struct v4l2_requestbuffers rb = {};
> > + int ret;
> > +
> > + rb.count = count;
> > + rb.type = bufferType_;
> > + rb.memory = memoryType_;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_REQBUFS, &rb);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Unable to request " << count << " buffers: "
> > + << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + LOG(V4L2, Debug) << rb.count << " buffers requested.";
> > +
> > + return rb.count;
> > +}
> > +
> > +/**
> > + * \brief Request buffers to be allocated from the device and stored in the
> > + * buffer pool provided.
> > + * \param[out] pool BufferPool to populate with buffers
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::exportBuffers(BufferPool *pool)
> > +{
> > + unsigned int allocatedBuffers;
> > + unsigned int i;
> > + int ret;
> > +
> > + memoryType_ = V4L2_MEMORY_MMAP;
> > +
> > + ret = requestBuffers(pool->count());
> > + if (ret < 0)
> > + return ret;
> > +
> > + allocatedBuffers = ret;
> > + if (allocatedBuffers < pool->count()) {
> > + LOG(V4L2, Error) << "Not enough buffers provided by V4L2Videodev";
> > + requestBuffers(0);
> > + return -ENOMEM;
> > + }
> > +
> > + /* Map the buffers. */
> > + for (i = 0; i < pool->count(); ++i) {
> > + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
> > + struct v4l2_buffer buf = {};
> > + Buffer &buffer = pool->buffers()[i];
> > +
> > + buf.index = i;
> > + buf.type = bufferType_;
> > + buf.memory = memoryType_;
> > + buf.length = VIDEO_MAX_PLANES;
> > + buf.m.planes = planes;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_QUERYBUF, &buf);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Unable to query buffer " << i << ": "
> > + << strerror(-ret);
> > + break;
> > + }
> > +
> > + if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
> > + for (unsigned int p = 0; p < buf.length; ++p) {
> > + ret = createPlane(&buffer, p,
> > + buf.m.planes[p].length);
> > + if (ret)
> > + break;
> > + }
> > + } else {
> > + ret = createPlane(&buffer, 0, buf.length);
> > + }
> > +
> > + if (ret) {
> > + LOG(V4L2, Error) << "Failed to create plane";
> > + break;
> > + }
> > + }
> > +
> > + if (ret) {
> > + requestBuffers(0);
> > + pool->destroyBuffers();
> > + return ret;
> > + }
> > +
> > + bufferPool_ = pool;
> > +
> > + return 0;
> > +}
> > +
> > +int V4L2Videodev::createPlane(Buffer *buffer, unsigned int planeIndex,
> > + unsigned int length)
> > +{
> > + struct v4l2_exportbuffer expbuf = {};
> > + int ret;
> > +
> > + LOG(V4L2, Debug)
> > + << "Buffer " << buffer->index()
> > + << " plane " << planeIndex
> > + << ": length=" << length;
> > +
> > + expbuf.type = bufferType_;
> > + expbuf.index = buffer->index();
> > + expbuf.plane = planeIndex;
> > + expbuf.flags = O_RDWR;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_EXPBUF, &expbuf);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Failed to export buffer: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + buffer->planes().emplace_back();
> > + Plane &plane = buffer->planes().back();
> > + plane.setDmabuf(expbuf.fd, length);
> > + ::close(expbuf.fd);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * \brief Import the externally allocated \a pool of buffers
> > + * \param[in] pool BufferPool of buffers to import
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::importBuffers(BufferPool *pool)
> > +{
> > + unsigned int allocatedBuffers;
> > + int ret;
> > +
> > + memoryType_ = V4L2_MEMORY_DMABUF;
> > +
> > + ret = requestBuffers(pool->count());
> > + if (ret < 0)
> > + return ret;
> > +
> > + allocatedBuffers = ret;
> > + if (allocatedBuffers < pool->count()) {
> > + LOG(V4L2, Error)
> > + << "Not enough buffers provided by V4L2Videodev";
> > + requestBuffers(0);
> > + return -ENOMEM;
> > + }
> > +
> > + LOG(V4L2, Debug) << "provided pool of " << pool->count() << " buffers";
> > + bufferPool_ = pool;
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * \brief Release all internally allocated buffers
> > + */
> > +int V4L2Videodev::releaseBuffers()
> > +{
> > + LOG(V4L2, Debug) << "Releasing bufferPool";
> > +
> > + bufferPool_ = nullptr;
> > +
> > + return requestBuffers(0);
> > +}
> > +
> > +/**
> > + * \brief Queue a buffer into the device
> > + * \param[in] buffer The buffer to be queued
> > + *
> > + * For capture devices the \a buffer will be filled with data by the device.
> > + * For output devices the \a buffer shall contain valid data and will be
> > + * processed by the device. Once the device has finished processing the buffer,
> > + * it will be available for dequeue.
> > + *
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::queueBuffer(Buffer *buffer)
> > +{
> > + struct v4l2_buffer buf = {};
> > + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
> > + int ret;
> > +
> > + buf.index = buffer->index();
> > + buf.type = bufferType_;
> > + buf.memory = memoryType_;
> > + buf.field = V4L2_FIELD_NONE;
> > +
> > + bool multiPlanar = V4L2_TYPE_IS_MULTIPLANAR(buf.type);
> > +
> > + if (buf.memory == V4L2_MEMORY_DMABUF) {
> > + if (multiPlanar) {
> > + for (unsigned int p = 0;
> > + p < buffer->planes().size();
> > + p++)
> > + planes[p].m.fd = buffer->planes()[p].dmabuf();
> > + } else {
> > + buf.m.fd = buffer->planes()[0].dmabuf();
> > + }
> > + }
> > +
> > + if (multiPlanar) {
> > + buf.length = buffer->planes().size();
> > + buf.m.planes = planes;
> > + }
> > +
> > + if (V4L2_TYPE_IS_OUTPUT(bufferType_)) {
> > + buf.bytesused = buffer->bytesused_;
> > + buf.sequence = buffer->sequence_;
> > + buf.timestamp.tv_sec = buffer->timestamp_ / 1000000000;
> > + buf.timestamp.tv_usec = (buffer->timestamp_ / 1000) % 1000000;
> > + }
> > +
> > + LOG(V4L2, Debug) << "Queueing buffer " << buf.index;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_QBUF, &buf);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Failed to queue buffer " << buf.index << ": "
> > + << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + if (queuedBuffersCount_++ == 0)
> > + fdEvent_->setEnabled(true);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * \brief Dequeue the next available buffer from the device
> > + *
> > + * This method dequeues the next available buffer from the device. If no buffer
> > + * is available to be dequeued it will return nullptr immediately.
> > + *
> > + * \return A pointer to the dequeued buffer on success, or nullptr otherwise
> > + */
> > +Buffer *V4L2Videodev::dequeueBuffer()
> > +{
> > + struct v4l2_buffer buf = {};
> > + struct v4l2_plane planes[VIDEO_MAX_PLANES] = {};
> > + int ret;
> > +
> > + buf.type = bufferType_;
> > + buf.memory = memoryType_;
> > +
> > + if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
> > + buf.length = VIDEO_MAX_PLANES;
> > + buf.m.planes = planes;
> > + }
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_DQBUF, &buf);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Failed to dequeue buffer: " << strerror(-ret);
> > + return nullptr;
> > + }
> > +
> > + ASSERT(buf.index < bufferPool_->count());
> > +
> > + if (--queuedBuffersCount_ == 0)
> > + fdEvent_->setEnabled(false);
> > +
> > + Buffer *buffer = &bufferPool_->buffers()[buf.index];
> > +
> > + buffer->bytesused_ = buf.bytesused;
> > + buffer->timestamp_ = buf.timestamp.tv_sec * 1000000000ULL
> > + + buf.timestamp.tv_usec * 1000ULL;
> > + buffer->sequence_ = buf.sequence;
> > + buffer->status_ = buf.flags & V4L2_BUF_FLAG_ERROR
> > + ? Buffer::BufferError : Buffer::BufferSuccess;
> > +
> > + return buffer;
> > +}
> > +
> > +/**
> > + * \brief Slot to handle completed buffer events from the V4L2 device
> > + * \param[in] notifier The event notifier
> > + *
> > + * When this slot is called, a Buffer has become available from the device, and
> > + * will be emitted through the bufferReady Signal.
> > + *
> > + * For Capture devices the Buffer will contain valid data.
> > + * For Output devices the Buffer can be considered empty.
> > + */
> > +void V4L2Videodev::bufferAvailable(EventNotifier *notifier)
> > +{
> > + Buffer *buffer = dequeueBuffer();
> > + if (!buffer)
> > + return;
> > +
> > + LOG(V4L2, Debug) << "Buffer " << buffer->index() << " is available";
> > +
> > + /* Notify anyone listening to the device. */
> > + bufferReady.emit(buffer);
> > +}
> > +
> > +/**
> > + * \var V4L2Videodev::bufferReady
> > + * \brief A Signal emitted when a buffer completes
> > + */
> > +
> > +/**
> > + * \brief Start the video stream
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::streamOn()
> > +{
> > + int ret;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_STREAMON, &bufferType_);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Failed to start streaming: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * \brief Stop the video stream
> > + *
> > + * Buffers that are still queued when the video stream is stopped are
> > + * implicitly dequeued, but no bufferReady signal is emitted for them.
> > + *
> > + * \return 0 on success or a negative error code otherwise
> > + */
> > +int V4L2Videodev::streamOff()
> > +{
> > + int ret;
> > +
> > + ret = V4L2Device::ioctl(VIDIOC_STREAMOFF, &bufferType_);
> > + if (ret < 0) {
> > + ret = -errno;
> > + LOG(V4L2, Error)
> > + << "Failed to stop streaming: " << strerror(-ret);
> > + return ret;
> > + }
> > +
> > + queuedBuffersCount_ = 0;
> > + fdEvent_->setEnabled(false);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * \brief Create a new video device instance from \a entity in media device
> > + * \a media
> > + * \param[in] media The media device where the entity is registered
> > + * \param[in] entity The media entity name
> > + *
> > + * Releasing memory of the newly created instance is responsibility of the
> > + * caller of this function.
> > + *
> > + * \return A newly created V4L2Videodev on success, nullptr otherwise
> > + */
> > +V4L2Videodev *V4L2Videodev::fromEntityName(const MediaDevice *media,
> > + const std::string &entity)
> > +{
> > + MediaEntity *mediaEntity = media->getEntityByName(entity);
> > + if (!mediaEntity)
> > + return nullptr;
> > +
> > + return new V4L2Videodev(mediaEntity);
> > +}
> > +
> > +} /* namespace libcamera */
> > diff --git a/test/meson.build b/test/meson.build
> > index 609aeab80e7d..f23b560afc73 100644
> > --- a/test/meson.build
> > +++ b/test/meson.build
> > @@ -4,8 +4,8 @@ subdir('camera')
> > subdir('ipa')
> > subdir('media_device')
> > subdir('pipeline')
> > -subdir('v4l2_device')
> > -subdir('v4l2_subdevice')
> > +subdir('v4l2_videodev')
> > +subdir('v4l2_subdev')
> >
> > public_tests = [
> > ['event', 'event.cpp'],
> > diff --git a/test/v4l2_subdevice/list_formats.cpp b/test/v4l2_subdev/list_formats.cpp
> > similarity index 95%
> > rename from test/v4l2_subdevice/list_formats.cpp
> > rename to test/v4l2_subdev/list_formats.cpp
> > index 3f0edafcdcd7..cd9d8b58850d 100644
> > --- a/test/v4l2_subdevice/list_formats.cpp
> > +++ b/test/v4l2_subdev/list_formats.cpp
> > @@ -11,15 +11,15 @@
> >
> > #include <libcamera/geometry.h>
> >
> > -#include "v4l2_subdevice.h"
> > -#include "v4l2_subdevice_test.h"
> > +#include "v4l2_subdev.h"
> > +#include "v4l2_subdev_test.h"
> >
> > using namespace std;
> > using namespace libcamera;
> >
> > /* List image formats on the "Scaler" subdevice of vimc media device. */
> >
> > -class ListFormatsTest : public V4L2SubdeviceTest
> > +class ListFormatsTest : public V4L2SubdevTest
> > {
> > protected:
> > int run() override;
> > diff --git a/test/v4l2_subdevice/meson.build b/test/v4l2_subdev/meson.build
> > similarity index 57%
> > rename from test/v4l2_subdevice/meson.build
> > rename to test/v4l2_subdev/meson.build
> > index 0521984b2a78..57fbead2170e 100644
> > --- a/test/v4l2_subdevice/meson.build
> > +++ b/test/v4l2_subdev/meson.build
> > @@ -1,12 +1,12 @@
> > -v4l2_subdevice_tests = [
> > +v4l2_subdev_tests = [
> > [ 'list_formats', 'list_formats.cpp'],
> > [ 'test_formats', 'test_formats.cpp'],
> > ]
> >
> > -foreach t : v4l2_subdevice_tests
> > - exe = executable(t[0], [t[1], 'v4l2_subdevice_test.cpp'],
> > +foreach t : v4l2_subdev_tests
> > + exe = executable(t[0], [t[1], 'v4l2_subdev_test.cpp'],
> > dependencies : libcamera_dep,
> > link_with : test_libraries,
> > include_directories : test_includes_internal)
> > - test(t[0], exe, suite : 'v4l2_subdevice', is_parallel : false)
> > + test(t[0], exe, suite : 'v4l2_subdev', is_parallel : false)
> > endforeach
> > diff --git a/test/v4l2_subdevice/test_formats.cpp b/test/v4l2_subdev/test_formats.cpp
> > similarity index 93%
> > rename from test/v4l2_subdevice/test_formats.cpp
> > rename to test/v4l2_subdev/test_formats.cpp
> > index e90c2c2426da..819264c1a97e 100644
> > --- a/test/v4l2_subdevice/test_formats.cpp
> > +++ b/test/v4l2_subdev/test_formats.cpp
> > @@ -8,15 +8,15 @@
> > #include <climits>
> > #include <iostream>
> >
> > -#include "v4l2_subdevice.h"
> > -#include "v4l2_subdevice_test.h"
> > +#include "v4l2_subdev.h"
> > +#include "v4l2_subdev_test.h"
> >
> > using namespace std;
> > using namespace libcamera;
> >
> > /* Test format handling on the "Scaler" subdevice of vimc media device. */
> >
> > -class FormatHandlingTest : public V4L2SubdeviceTest
> > +class FormatHandlingTest : public V4L2SubdevTest
> > {
> > protected:
> > int run() override;
> > diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.cpp b/test/v4l2_subdev/v4l2_subdev_test.cpp
> > similarity index 84%
> > rename from test/v4l2_subdevice/v4l2_subdevice_test.cpp
> > rename to test/v4l2_subdev/v4l2_subdev_test.cpp
> > index 562a638cb28e..c9f26c5f88f4 100644
> > --- a/test/v4l2_subdevice/v4l2_subdevice_test.cpp
> > +++ b/test/v4l2_subdev/v4l2_subdev_test.cpp
> > @@ -2,7 +2,7 @@
> > /*
> > * Copyright (C) 2019, Google Inc.
> > *
> > - * v4l2_subdevice_test.cpp - VIMC-based V4L2 subdevice test
> > + * v4l2_subdev_test.cpp - VIMC-based V4L2 subdevice test
> > */
> >
> > #include <iostream>
> > @@ -11,8 +11,8 @@
> >
> > #include "device_enumerator.h"
> > #include "media_device.h"
> > -#include "v4l2_subdevice.h"
> > -#include "v4l2_subdevice_test.h"
> > +#include "v4l2_subdev.h"
> > +#include "v4l2_subdev_test.h"
> >
> > using namespace std;
> > using namespace libcamera;
> > @@ -25,7 +25,7 @@ using namespace libcamera;
> > * If the vimc module is not loaded, the test gets skipped.
> > */
> >
> > -int V4L2SubdeviceTest::init()
> > +int V4L2SubdevTest::init()
> > {
> > enumerator_ = DeviceEnumerator::create();
> > if (!enumerator_) {
> > @@ -51,7 +51,7 @@ int V4L2SubdeviceTest::init()
> > return TestFail;
> > }
> >
> > - scaler_ = new V4L2Subdevice(videoEntity);
> > + scaler_ = new V4L2Subdev(videoEntity);
> > if (scaler_->open()) {
> > cerr << "Unable to open video subdevice "
> > << scaler_->entity()->deviceNode() << endl;
> > @@ -61,7 +61,7 @@ int V4L2SubdeviceTest::init()
> > return 0;
> > }
> >
> > -void V4L2SubdeviceTest::cleanup()
> > +void V4L2SubdevTest::cleanup()
> > {
> > delete scaler_;
> > }
> > diff --git a/test/v4l2_subdevice/v4l2_subdevice_test.h b/test/v4l2_subdev/v4l2_subdev_test.h
> > similarity index 76%
> > rename from test/v4l2_subdevice/v4l2_subdevice_test.h
> > rename to test/v4l2_subdev/v4l2_subdev_test.h
> > index 96646a155536..9e332daf9aec 100644
> > --- a/test/v4l2_subdevice/v4l2_subdevice_test.h
> > +++ b/test/v4l2_subdev/v4l2_subdev_test.h
> > @@ -2,7 +2,7 @@
> > /*
> > * Copyright (C) 2019, Google Inc.
> > *
> > - * v4l2_subdevice_test.h - VIMC-based V4L2 subdevice test
> > + * v4l2_subdev_test.h - VIMC-based V4L2 subdevice test
> > */
> >
> > #ifndef __LIBCAMERA_V4L2_SUBDEVICE_TEST_H__
> > @@ -13,14 +13,14 @@
> > #include "device_enumerator.h"
> > #include "media_device.h"
> > #include "test.h"
> > -#include "v4l2_subdevice.h"
> > +#include "v4l2_subdev.h"
> >
> > using namespace libcamera;
> >
> > -class V4L2SubdeviceTest : public Test
> > +class V4L2SubdevTest : public Test
> > {
> > public:
> > - V4L2SubdeviceTest()
> > + V4L2SubdevTest()
> > : scaler_(nullptr){};
> >
> > protected:
> > @@ -29,7 +29,7 @@ protected:
> >
> > std::unique_ptr<DeviceEnumerator> enumerator_;
> > std::shared_ptr<MediaDevice> media_;
> > - V4L2Subdevice *scaler_;
> > + V4L2Subdev *scaler_;
> > };
> >
> > #endif /* __LIBCAMERA_V4L2_SUBDEVICE_TEST_H__ */
> > diff --git a/test/v4l2_device/buffer_sharing.cpp b/test/v4l2_videodev/buffer_sharing.cpp
> > similarity index 90%
> > rename from test/v4l2_device/buffer_sharing.cpp
> > rename to test/v4l2_videodev/buffer_sharing.cpp
> > index e63ddff85689..a997cd69af5c 100644
> > --- a/test/v4l2_device/buffer_sharing.cpp
> > +++ b/test/v4l2_videodev/buffer_sharing.cpp
> > @@ -4,8 +4,8 @@
> > *
> > * libcamera V4L2 API tests
> > *
> > - * Validate the function of exporting buffers from a V4L2Device and
> > - * the ability to import them to another V4L2Device instance.
> > + * Validate the function of exporting buffers from a V4L2Videodev and
> > + * the ability to import them to another V4L2Videodev instance.
> > * Ensure that the Buffers can successfully be queued and dequeued
> > * between both devices.
> > */
> > @@ -17,19 +17,19 @@
> > #include <libcamera/event_dispatcher.h>
> > #include <libcamera/timer.h>
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > -class BufferSharingTest : public V4L2DeviceTest
> > +class BufferSharingTest : public V4L2VideodevTest
> > {
> > public:
> > BufferSharingTest()
> > - : V4L2DeviceTest("vivid", "vivid-000-vid-cap"),
> > + : V4L2VideodevTest("vivid", "vivid-000-vid-cap"),
> > output_(nullptr), framesCaptured_(0), framesOutput_(0) {}
> >
> > protected:
> > int init()
> > {
> > - int ret = V4L2DeviceTest::init();
> > + int ret = V4L2VideodevTest::init();
> > if (ret)
> > return ret;
> >
> > @@ -38,7 +38,7 @@ protected:
> > if (!entity)
> > return TestSkip;
> >
> > - output_ = new V4L2Device(entity);
> > + output_ = new V4L2Videodev(entity);
> > if (!output_) {
> > std::cout << "Failed to create output device" << std::endl;
> > return TestFail;
> > @@ -171,13 +171,13 @@ protected:
> >
> > delete output_;
> >
> > - V4L2DeviceTest::cleanup();
> > + V4L2VideodevTest::cleanup();
> > }
> >
> > private:
> > const unsigned int bufferCount = 4;
> >
> > - V4L2Device *output_;
> > + V4L2Videodev *output_;
> >
> > unsigned int framesCaptured_;
> > unsigned int framesOutput_;
> > diff --git a/test/v4l2_device/capture_async.cpp b/test/v4l2_videodev/capture_async.cpp
> > similarity index 92%
> > rename from test/v4l2_device/capture_async.cpp
> > rename to test/v4l2_videodev/capture_async.cpp
> > index 69b1d5a13ed8..97b2a9d9f246 100644
> > --- a/test/v4l2_device/capture_async.cpp
> > +++ b/test/v4l2_videodev/capture_async.cpp
> > @@ -12,13 +12,13 @@
> >
> > #include <iostream>
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > -class CaptureAsyncTest : public V4L2DeviceTest
> > +class CaptureAsyncTest : public V4L2VideodevTest
> > {
> > public:
> > CaptureAsyncTest()
> > - : V4L2DeviceTest("vimc", "Raw Capture 0"), frames(0) {}
> > + : V4L2VideodevTest("vimc", "Raw Capture 0"), frames(0) {}
> >
> > void receiveBuffer(Buffer *buffer)
> > {
> > diff --git a/test/v4l2_device/double_open.cpp b/test/v4l2_videodev/double_open.cpp
> > similarity index 76%
> > rename from test/v4l2_device/double_open.cpp
> > rename to test/v4l2_videodev/double_open.cpp
> > index 53850620889b..b679be3b3319 100644
> > --- a/test/v4l2_device/double_open.cpp
> > +++ b/test/v4l2_videodev/double_open.cpp
> > @@ -7,15 +7,15 @@
> >
> > #include <iostream>
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > namespace {
> >
> > -class DoubleOpen : public V4L2DeviceTest
> > +class DoubleOpen : public V4L2VideodevTest
> > {
> > public:
> > DoubleOpen()
> > - : V4L2DeviceTest("vimc", "Raw Capture 0") {}
> > + : V4L2VideodevTest("vimc", "Raw Capture 0") {}
> > protected:
> > int run()
> > {
> > @@ -23,7 +23,7 @@ protected:
> >
> > /*
> > * Expect failure: The device has already been opened by the
> > - * V4L2DeviceTest base class
> > + * V4L2VideodevTest base class
> > */
> > ret = capture_->open();
> > if (!ret) {
> > diff --git a/test/v4l2_device/formats.cpp b/test/v4l2_videodev/formats.cpp
> > similarity index 86%
> > rename from test/v4l2_device/formats.cpp
> > rename to test/v4l2_videodev/formats.cpp
> > index 6be045ff754c..80b92a560c7a 100644
> > --- a/test/v4l2_device/formats.cpp
> > +++ b/test/v4l2_videodev/formats.cpp
> > @@ -8,18 +8,18 @@
> > #include <climits>
> > #include <iostream>
> >
> > -#include "v4l2_device.h"
> > +#include "v4l2_videodev.h"
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > using namespace std;
> > using namespace libcamera;
> >
> > -class Format : public V4L2DeviceTest
> > +class Format : public V4L2VideodevTest
> > {
> > public:
> > Format()
> > - : V4L2DeviceTest("vimc", "Raw Capture 0") {}
> > + : V4L2VideodevTest("vimc", "Raw Capture 0") {}
> > protected:
> > int run()
> > {
> > diff --git a/test/v4l2_device/meson.build b/test/v4l2_videodev/meson.build
> > similarity index 75%
> > rename from test/v4l2_device/meson.build
> > rename to test/v4l2_videodev/meson.build
> > index de540b1ba6f5..34eb33d1c3ec 100644
> > --- a/test/v4l2_device/meson.build
> > +++ b/test/v4l2_videodev/meson.build
> > @@ -1,6 +1,6 @@
> > # Tests are listed in order of complexity.
> > # They are not alphabetically sorted.
> > -v4l2_device_tests = [
> > +v4l2_videodev_tests = [
> > [ 'double_open', 'double_open.cpp' ],
> > [ 'formats', 'formats.cpp' ],
> > [ 'request_buffers', 'request_buffers.cpp' ],
> > @@ -9,10 +9,10 @@ v4l2_device_tests = [
> > [ 'buffer_sharing', 'buffer_sharing.cpp' ],
> > ]
> >
> > -foreach t : v4l2_device_tests
> > - exe = executable(t[0], [t[1], 'v4l2_device_test.cpp'],
> > +foreach t : v4l2_videodev_tests
> > + exe = executable(t[0], [t[1], 'v4l2_videodev_test.cpp'],
> > dependencies : libcamera_dep,
> > link_with : test_libraries,
> > include_directories : test_includes_internal)
> > - test(t[0], exe, suite : 'v4l2_device', is_parallel : false)
> > + test(t[0], exe, suite : 'v4l2_videodev', is_parallel : false)
> > endforeach
> > diff --git a/test/v4l2_device/request_buffers.cpp b/test/v4l2_videodev/request_buffers.cpp
> > similarity index 78%
> > rename from test/v4l2_device/request_buffers.cpp
> > rename to test/v4l2_videodev/request_buffers.cpp
> > index 7b7b06b2b409..493605e79746 100644
> > --- a/test/v4l2_device/request_buffers.cpp
> > +++ b/test/v4l2_videodev/request_buffers.cpp
> > @@ -5,13 +5,13 @@
> > * libcamera V4L2 API tests
> > */
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > -class RequestBuffersTest : public V4L2DeviceTest
> > +class RequestBuffersTest : public V4L2VideodevTest
> > {
> > public:
> > RequestBuffersTest()
> > - : V4L2DeviceTest("vimc", "Raw Capture 0") {}
> > + : V4L2VideodevTest("vimc", "Raw Capture 0") {}
> >
> > protected:
> > int run()
> > diff --git a/test/v4l2_device/stream_on_off.cpp b/test/v4l2_videodev/stream_on_off.cpp
> > similarity index 79%
> > rename from test/v4l2_device/stream_on_off.cpp
> > rename to test/v4l2_videodev/stream_on_off.cpp
> > index b158b8e402c8..fefe48c7ad39 100644
> > --- a/test/v4l2_device/stream_on_off.cpp
> > +++ b/test/v4l2_videodev/stream_on_off.cpp
> > @@ -5,13 +5,13 @@
> > * libcamera V4L2 API tests
> > */
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > -class StreamOnStreamOffTest : public V4L2DeviceTest
> > +class StreamOnStreamOffTest : public V4L2VideodevTest
> > {
> > public:
> > StreamOnStreamOffTest()
> > - : V4L2DeviceTest("vimc", "Raw Capture 0") {}
> > + : V4L2VideodevTest("vimc", "Raw Capture 0") {}
> > protected:
> > int run()
> > {
> > diff --git a/test/v4l2_device/v4l2_device_test.cpp b/test/v4l2_videodev/v4l2_videodev_test.cpp
> > similarity index 91%
> > rename from test/v4l2_device/v4l2_device_test.cpp
> > rename to test/v4l2_videodev/v4l2_videodev_test.cpp
> > index baad48f8aa9e..b3ed503567ee 100644
> > --- a/test/v4l2_device/v4l2_device_test.cpp
> > +++ b/test/v4l2_videodev/v4l2_videodev_test.cpp
> > @@ -8,7 +8,7 @@
> > #include <iostream>
> > #include <sys/stat.h>
> >
> > -#include "v4l2_device_test.h"
> > +#include "v4l2_videodev_test.h"
> >
> > #include "device_enumerator.h"
> > #include "media_device.h"
> > @@ -26,7 +26,7 @@ bool exists(const std::string &path)
> > return false;
> > }
> >
> > -int V4L2DeviceTest::init()
> > +int V4L2VideodevTest::init()
> > {
> > enumerator_ = DeviceEnumerator::create();
> > if (!enumerator_) {
> > @@ -50,7 +50,7 @@ int V4L2DeviceTest::init()
> > if (!entity)
> > return TestSkip;
> >
> > - capture_ = new V4L2Device(entity);
> > + capture_ = new V4L2Videodev(entity);
> > if (!capture_)
> > return TestFail;
> >
> > @@ -77,7 +77,7 @@ int V4L2DeviceTest::init()
> > return TestPass;
> > }
> >
> > -void V4L2DeviceTest::cleanup()
> > +void V4L2VideodevTest::cleanup()
> > {
> > capture_->streamOff();
> > capture_->releaseBuffers();
> > diff --git a/test/v4l2_device/v4l2_device_test.h b/test/v4l2_videodev/v4l2_videodev_test.h
> > similarity index 82%
> > rename from test/v4l2_device/v4l2_device_test.h
> > rename to test/v4l2_videodev/v4l2_videodev_test.h
> > index 651c005f4e54..6298961b3b15 100644
> > --- a/test/v4l2_device/v4l2_device_test.h
> > +++ b/test/v4l2_videodev/v4l2_videodev_test.h
> > @@ -15,14 +15,14 @@
> >
> > #include "device_enumerator.h"
> > #include "media_device.h"
> > -#include "v4l2_device.h"
> > +#include "v4l2_videodev.h"
> >
> > using namespace libcamera;
> >
> > -class V4L2DeviceTest : public Test
> > +class V4L2VideodevTest : public Test
> > {
> > public:
> > - V4L2DeviceTest(const char *driver, const char *entity)
> > + V4L2VideodevTest(const char *driver, const char *entity)
> > : driver_(driver), entity_(entity), capture_(nullptr)
> > {
> > }
> > @@ -35,7 +35,7 @@ protected:
> > std::string entity_;
> > std::unique_ptr<DeviceEnumerator> enumerator_;
> > std::shared_ptr<MediaDevice> media_;
> > - V4L2Device *capture_;
> > + V4L2Videodev *capture_;
> > BufferPool pool_;
> > };
> >
> > --
> > 2.21.0
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel at lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
> >
>
> --
> Regards
> --
> Kieran
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.libcamera.org/pipermail/libcamera-devel/attachments/20190612/3fb759bd/attachment-0001.sig>
More information about the libcamera-devel
mailing list