Message ID | 20250725-multicontext-v1-6-ea558291e101@ideasonboard.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
Hi Jacopo, Thank you for the patch! On Fri, 2025-07-25 at 12:33 +0200, Jacopo Mondi wrote: > Introduce the SharedMediaDevice class derived from the MediaDevice > class, that represents a media device that supports multi-context > operations. > > Disallow direct construction of MediaDevice and introduce the > MediaDeviceFactory class that instantiate the correct class from the > MediaDevice class hierarchy inspecting the newly introduced > media_device_info.flags field. > > The new SharedMediaDevice class represents a media device that can > be concurrently accessed by different processes, for this reason > overload the acquire/lock operations to be effectively nops. > > Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> > --- > include/libcamera/internal/media_device.h | 39 +++++++-- > src/libcamera/device_enumerator.cpp | 8 +- > src/libcamera/media_device.cpp | 139 > ++++++++++++++++++++++++++++++ > 3 files changed, 178 insertions(+), 8 deletions(-) > > diff --git a/include/libcamera/internal/media_device.h > b/include/libcamera/internal/media_device.h > index > b3a48b98d64b019fb42c5224d29105e1d57a3e3c..ea0e9d66009933bbb9613d27423 > d2f951520c887 100644 > --- a/include/libcamera/internal/media_device.h > +++ b/include/libcamera/internal/media_device.h > @@ -21,18 +21,24 @@ > > namespace libcamera { > > +class MediaDeviceFactory > +{ > +public: > + static std::unique_ptr<MediaDevice> > + createMediaDevice(const std::string &deviceNode); > +}; > + > class MediaDevice : protected Loggable > { > public: > - MediaDevice(const std::string &deviceNode); > - ~MediaDevice(); > + virtual ~MediaDevice(); > > - bool acquire(); > - void release(); > - bool busy() const { return acquired_; } > + virtual bool acquire(); > + virtual void release(); > + virtual bool busy() const { return acquired_; } > > - bool lock(); > - void unlock(); > + virtual bool lock(); > + virtual void unlock(); > > int populate(); > bool isValid() const { return valid_; } > @@ -58,9 +64,12 @@ public: > std::vector<MediaEntity *> locateEntities(unsigned int > function); > Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> > protected: > + MediaDevice(const std::string &deviceNode); > std::string logPrefix() const override; > > private: > + friend class MediaDeviceFactory; > + > int open(); > void close(); > > @@ -92,4 +101,20 @@ private: > std::vector<MediaEntity *> entities_; > }; > > +class SharedMediaDevice : public MediaDevice > +{ > +public: > + bool acquire() override; > + void release() override; > + bool busy() const override { return false; } > + > + bool lock() override; > + void unlock() override; > + > +private: > + friend class MediaDeviceFactory; > + > + SharedMediaDevice(const std::string &deviceNode); > +}; > + > } /* namespace libcamera */ > diff --git a/src/libcamera/device_enumerator.cpp > b/src/libcamera/device_enumerator.cpp > index > ae17862f676310ef568e5331106ed661e84b5130..e73bb0050843c5281986e8e05d2 > 013102905d270 100644 > --- a/src/libcamera/device_enumerator.cpp > +++ b/src/libcamera/device_enumerator.cpp > @@ -217,7 +217,13 @@ DeviceEnumerator::~DeviceEnumerator() > */ > std::unique_ptr<MediaDevice> DeviceEnumerator::createDevice(const > std::string &deviceNode) > { > - std::unique_ptr<MediaDevice> media = > std::make_unique<MediaDevice>(deviceNode); > + std::unique_ptr<MediaDevice> media = > MediaDeviceFactory::createMediaDevice(deviceNode); > + if (!media) { > + LOG(DeviceEnumerator, Info) > + << "Unable to create a media device for " << > deviceNode > + << ", skipping"; > + return nullptr; > + } > > int ret = media->populate(); > if (ret < 0) { > diff --git a/src/libcamera/media_device.cpp > b/src/libcamera/media_device.cpp > index > 353f34a81eca600a4a35594d782d3dc5dd690bdf..70d073f964bde0236585de083ba > ca4cd9c30d193 100644 > --- a/src/libcamera/media_device.cpp > +++ b/src/libcamera/media_device.cpp > @@ -31,6 +31,56 @@ namespace libcamera { > > LOG_DEFINE_CATEGORY(MediaDevice) > > +/** > + * \class MediaDeviceFactory > + * \brief Factory class that instantiates a MediaDevice or > SharedMediaDevice > + * > + * The MediaDevice and SharedMediaDevice classes cannot be > instantiated > + * directly, but instead the MediaDeviceFactory shall be used to > create > + * an instance of these classes using the > MediaDeviceFactory::createMediaDevice > + * function. > + */ > + > +/** > + * \brief Create an instance of the MediaDevice class hierarchy > + * \param[in] deviceNode The media device node path > + * > + * If a media device supports multi-context operations and advertise > it through > + * the presence of the MEDIA_DEVICE_FL_CONTEXT flag in the > + * media_device_info.flags field an instance of SharedMediaDevice > will be > + * created and returned. If instead the media device doesn's support > + * multi-context operations an instance of the MediaDevice class is > created and > + * returned. > + * > + * \return A unique_ptr<> that wraps an instance of > SharedMediaDevice or > + * MediaDevice > + */ > +std::unique_ptr<MediaDevice> > +MediaDeviceFactory::createMediaDevice(const std::string &deviceNode) > +{ > + /* > + * Inspect the media device info flags to decide which class > to > + * instantiate. > + */ > + auto fd = UniqueFD(::open(deviceNode.c_str(), O_RDWR | > O_CLOEXEC)); > + if (!fd.isValid()) > + return {}; > + > + struct media_device_info info = {}; > + int ret = ioctl(fd.get(), MEDIA_IOC_DEVICE_INFO, &info); > + if (ret) { > + LOG(MediaDevice, Error) > + << "Failed to get media device info " << > strerror(-ret); > + return {}; > + } > + > + if (info.flags & MEDIA_DEVICE_FL_CONTEXT) > + return std::unique_ptr<SharedMediaDevice> > + (new SharedMediaDevice(deviceNode)); > + > + return std::unique_ptr<MediaDevice>(new > MediaDevice(deviceNode)); > +} > + > /** > * \class MediaDevice > * \brief The MediaDevice represents a Media Controller device with > its full > @@ -40,6 +90,9 @@ LOG_DEFINE_CATEGORY(MediaDevice) > * created, and that association is kept for the lifetime of the > MediaDevice > * instance. > * > + * Instances of MediaDevice are created by the MediaDeviceFactory > + * createMediaDevice() function and cannot be directly instantiated. > + * > * The instance is created with an empty media graph. Before > performing any > * other operation, it must be populate by calling populate(). > Instances of > * MediaEntity, MediaPad and MediaLink are created to model the > media graph, > @@ -56,6 +109,30 @@ LOG_DEFINE_CATEGORY(MediaDevice) > * managers to claim media devices they support during enumeration. > */ > > +/** > + * \class SharedMediaDevice > + * \brief The SharedMediaDevice represents a Media Controller device > that > + * supports multi-context operations > + * > + * \sa MediaDevice > + * \sa MediaDeviceFactory > + * > + * A SharedMediaDevice is associated with a media controller device > node that > + * supports multi-context operations. Compared to the MediaDevice > base class a > + * SharedMediaDevice allows multiple open() calls to happen and is > never > + * uniquely owned as each time the media device gets open a new > context gets > + * created. This means that the acquire() and release() operations > are > + * effectively nop (and as a consequence busy() always return > false). > + * > + * Instances of MediaDevice are created by the MediaDeviceFactory > + * createMediaDevice() function and cannot be directly instantiated. > + * > + * Similarly to the MediaDevice class, instances of > SharedMediaDevice are > + * created with an empty media graph and needs to be populated using > the > + * populate() function. Media entities enumerated in the graph can > be accessed > + * through the same functions as the ones provided by the > MediaDevice class. > + */ > + > /** > * \brief Construct a MediaDevice > * \param[in] deviceNode The media device node path > @@ -68,6 +145,17 @@ MediaDevice::MediaDevice(const std::string > &deviceNode) > { > } > > +/** > + * \brief Construct a SharedMediaDevice > + * \param[in] deviceNode The media device node path > + * > + * \copydoc MediaDevice::MediaDevice > + */ > +SharedMediaDevice::SharedMediaDevice(const std::string &deviceNode) > + : MediaDevice(deviceNode) > +{ > +} > + > MediaDevice::~MediaDevice() > { > fd_.reset(); > @@ -113,6 +201,19 @@ bool MediaDevice::acquire() > return true; > } > > +/** > + * \brief Acquiring a SharedMediaDevice is a nop > + * > + * A SharedMediaDevice is designed to be opened multiple times. > Acquiring > + * a SharedMediaDevice is effectively a nop. > + * > + * \return Always return true > + */ > +bool SharedMediaDevice::acquire() > +{ > + return true; > +} > + > /** > * \brief Release a device previously claimed for exclusive use > * \sa acquire(), busy() > @@ -123,6 +224,15 @@ void MediaDevice::release() > acquired_ = false; > } > > +/** > + * \brief Releasing a SharedMediaDevice is a nop > + * > + * As acquiring a SharedMediaDevice is a nop, releasing it is a nop > as well. > + */ > +void SharedMediaDevice::release() > +{ > +} > + > /** > * \brief Lock the device to prevent it from being used by other > instances of > * libcamera > @@ -151,6 +261,19 @@ bool MediaDevice::lock() > return true; > } > > +/** > + * \brief Locking a SharedMediaDevice is a nop > + * > + * As SharedMediaDevice is designed to be opened multiple times > locking > + * a SharedMediaDevice is effectively a nop. > + * > + * \return Always return true > + */ > +bool SharedMediaDevice::lock() > +{ > + return true; > +} > + > /** > * \brief Unlock the device and free it for use for libcamera > instances > * > @@ -168,6 +291,15 @@ void MediaDevice::unlock() > std::ignore = lockf(fd_.get(), F_ULOCK, 0); > } > > +/** > + * \brief Unlocking a SharedMediaDevice is a nop > + * > + * As locking a SharedMediaDevice is a nop, unlocking it is a nop as > well. > + */ > +void SharedMediaDevice::unlock() > +{ > +} > + > /** > * \fn MediaDevice::busy() > * \brief Check if a device is in use > @@ -176,6 +308,13 @@ void MediaDevice::unlock() > * \sa acquire(), release() > */ > > +/** > + * \fn SharedMediaDevice::busy() > + * \brief As SharedMediaDevice is designed to be opened multiple > times, for this > + * reason it is never marked as busy > + * \return Always returns false > + */ > + > /** > * \brief Populate the MediaDevice with device information and media > objects > *
diff --git a/include/libcamera/internal/media_device.h b/include/libcamera/internal/media_device.h index b3a48b98d64b019fb42c5224d29105e1d57a3e3c..ea0e9d66009933bbb9613d27423d2f951520c887 100644 --- a/include/libcamera/internal/media_device.h +++ b/include/libcamera/internal/media_device.h @@ -21,18 +21,24 @@ namespace libcamera { +class MediaDeviceFactory +{ +public: + static std::unique_ptr<MediaDevice> + createMediaDevice(const std::string &deviceNode); +}; + class MediaDevice : protected Loggable { public: - MediaDevice(const std::string &deviceNode); - ~MediaDevice(); + virtual ~MediaDevice(); - bool acquire(); - void release(); - bool busy() const { return acquired_; } + virtual bool acquire(); + virtual void release(); + virtual bool busy() const { return acquired_; } - bool lock(); - void unlock(); + virtual bool lock(); + virtual void unlock(); int populate(); bool isValid() const { return valid_; } @@ -58,9 +64,12 @@ public: std::vector<MediaEntity *> locateEntities(unsigned int function); protected: + MediaDevice(const std::string &deviceNode); std::string logPrefix() const override; private: + friend class MediaDeviceFactory; + int open(); void close(); @@ -92,4 +101,20 @@ private: std::vector<MediaEntity *> entities_; }; +class SharedMediaDevice : public MediaDevice +{ +public: + bool acquire() override; + void release() override; + bool busy() const override { return false; } + + bool lock() override; + void unlock() override; + +private: + friend class MediaDeviceFactory; + + SharedMediaDevice(const std::string &deviceNode); +}; + } /* namespace libcamera */ diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp index ae17862f676310ef568e5331106ed661e84b5130..e73bb0050843c5281986e8e05d2013102905d270 100644 --- a/src/libcamera/device_enumerator.cpp +++ b/src/libcamera/device_enumerator.cpp @@ -217,7 +217,13 @@ DeviceEnumerator::~DeviceEnumerator() */ std::unique_ptr<MediaDevice> DeviceEnumerator::createDevice(const std::string &deviceNode) { - std::unique_ptr<MediaDevice> media = std::make_unique<MediaDevice>(deviceNode); + std::unique_ptr<MediaDevice> media = MediaDeviceFactory::createMediaDevice(deviceNode); + if (!media) { + LOG(DeviceEnumerator, Info) + << "Unable to create a media device for " << deviceNode + << ", skipping"; + return nullptr; + } int ret = media->populate(); if (ret < 0) { diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp index 353f34a81eca600a4a35594d782d3dc5dd690bdf..70d073f964bde0236585de083baca4cd9c30d193 100644 --- a/src/libcamera/media_device.cpp +++ b/src/libcamera/media_device.cpp @@ -31,6 +31,56 @@ namespace libcamera { LOG_DEFINE_CATEGORY(MediaDevice) +/** + * \class MediaDeviceFactory + * \brief Factory class that instantiates a MediaDevice or SharedMediaDevice + * + * The MediaDevice and SharedMediaDevice classes cannot be instantiated + * directly, but instead the MediaDeviceFactory shall be used to create + * an instance of these classes using the MediaDeviceFactory::createMediaDevice + * function. + */ + +/** + * \brief Create an instance of the MediaDevice class hierarchy + * \param[in] deviceNode The media device node path + * + * If a media device supports multi-context operations and advertise it through + * the presence of the MEDIA_DEVICE_FL_CONTEXT flag in the + * media_device_info.flags field an instance of SharedMediaDevice will be + * created and returned. If instead the media device doesn's support + * multi-context operations an instance of the MediaDevice class is created and + * returned. + * + * \return A unique_ptr<> that wraps an instance of SharedMediaDevice or + * MediaDevice + */ +std::unique_ptr<MediaDevice> +MediaDeviceFactory::createMediaDevice(const std::string &deviceNode) +{ + /* + * Inspect the media device info flags to decide which class to + * instantiate. + */ + auto fd = UniqueFD(::open(deviceNode.c_str(), O_RDWR | O_CLOEXEC)); + if (!fd.isValid()) + return {}; + + struct media_device_info info = {}; + int ret = ioctl(fd.get(), MEDIA_IOC_DEVICE_INFO, &info); + if (ret) { + LOG(MediaDevice, Error) + << "Failed to get media device info " << strerror(-ret); + return {}; + } + + if (info.flags & MEDIA_DEVICE_FL_CONTEXT) + return std::unique_ptr<SharedMediaDevice> + (new SharedMediaDevice(deviceNode)); + + return std::unique_ptr<MediaDevice>(new MediaDevice(deviceNode)); +} + /** * \class MediaDevice * \brief The MediaDevice represents a Media Controller device with its full @@ -40,6 +90,9 @@ LOG_DEFINE_CATEGORY(MediaDevice) * created, and that association is kept for the lifetime of the MediaDevice * instance. * + * Instances of MediaDevice are created by the MediaDeviceFactory + * createMediaDevice() function and cannot be directly instantiated. + * * The instance is created with an empty media graph. Before performing any * other operation, it must be populate by calling populate(). Instances of * MediaEntity, MediaPad and MediaLink are created to model the media graph, @@ -56,6 +109,30 @@ LOG_DEFINE_CATEGORY(MediaDevice) * managers to claim media devices they support during enumeration. */ +/** + * \class SharedMediaDevice + * \brief The SharedMediaDevice represents a Media Controller device that + * supports multi-context operations + * + * \sa MediaDevice + * \sa MediaDeviceFactory + * + * A SharedMediaDevice is associated with a media controller device node that + * supports multi-context operations. Compared to the MediaDevice base class a + * SharedMediaDevice allows multiple open() calls to happen and is never + * uniquely owned as each time the media device gets open a new context gets + * created. This means that the acquire() and release() operations are + * effectively nop (and as a consequence busy() always return false). + * + * Instances of MediaDevice are created by the MediaDeviceFactory + * createMediaDevice() function and cannot be directly instantiated. + * + * Similarly to the MediaDevice class, instances of SharedMediaDevice are + * created with an empty media graph and needs to be populated using the + * populate() function. Media entities enumerated in the graph can be accessed + * through the same functions as the ones provided by the MediaDevice class. + */ + /** * \brief Construct a MediaDevice * \param[in] deviceNode The media device node path @@ -68,6 +145,17 @@ MediaDevice::MediaDevice(const std::string &deviceNode) { } +/** + * \brief Construct a SharedMediaDevice + * \param[in] deviceNode The media device node path + * + * \copydoc MediaDevice::MediaDevice + */ +SharedMediaDevice::SharedMediaDevice(const std::string &deviceNode) + : MediaDevice(deviceNode) +{ +} + MediaDevice::~MediaDevice() { fd_.reset(); @@ -113,6 +201,19 @@ bool MediaDevice::acquire() return true; } +/** + * \brief Acquiring a SharedMediaDevice is a nop + * + * A SharedMediaDevice is designed to be opened multiple times. Acquiring + * a SharedMediaDevice is effectively a nop. + * + * \return Always return true + */ +bool SharedMediaDevice::acquire() +{ + return true; +} + /** * \brief Release a device previously claimed for exclusive use * \sa acquire(), busy() @@ -123,6 +224,15 @@ void MediaDevice::release() acquired_ = false; } +/** + * \brief Releasing a SharedMediaDevice is a nop + * + * As acquiring a SharedMediaDevice is a nop, releasing it is a nop as well. + */ +void SharedMediaDevice::release() +{ +} + /** * \brief Lock the device to prevent it from being used by other instances of * libcamera @@ -151,6 +261,19 @@ bool MediaDevice::lock() return true; } +/** + * \brief Locking a SharedMediaDevice is a nop + * + * As SharedMediaDevice is designed to be opened multiple times locking + * a SharedMediaDevice is effectively a nop. + * + * \return Always return true + */ +bool SharedMediaDevice::lock() +{ + return true; +} + /** * \brief Unlock the device and free it for use for libcamera instances * @@ -168,6 +291,15 @@ void MediaDevice::unlock() std::ignore = lockf(fd_.get(), F_ULOCK, 0); } +/** + * \brief Unlocking a SharedMediaDevice is a nop + * + * As locking a SharedMediaDevice is a nop, unlocking it is a nop as well. + */ +void SharedMediaDevice::unlock() +{ +} + /** * \fn MediaDevice::busy() * \brief Check if a device is in use @@ -176,6 +308,13 @@ void MediaDevice::unlock() * \sa acquire(), release() */ +/** + * \fn SharedMediaDevice::busy() + * \brief As SharedMediaDevice is designed to be opened multiple times, for this + * reason it is never marked as busy + * \return Always returns false + */ + /** * \brief Populate the MediaDevice with device information and media objects *
Introduce the SharedMediaDevice class derived from the MediaDevice class, that represents a media device that supports multi-context operations. Disallow direct construction of MediaDevice and introduce the MediaDeviceFactory class that instantiate the correct class from the MediaDevice class hierarchy inspecting the newly introduced media_device_info.flags field. The new SharedMediaDevice class represents a media device that can be concurrently accessed by different processes, for this reason overload the acquire/lock operations to be effectively nops. Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> --- include/libcamera/internal/media_device.h | 39 +++++++-- src/libcamera/device_enumerator.cpp | 8 +- src/libcamera/media_device.cpp | 139 ++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 8 deletions(-)