[7/9] libcamera: media-device: Introduce MediaContext
diff mbox series

Message ID 20250725-multicontext-v1-7-ea558291e101@ideasonboard.com
State New
Headers show
Series
  • libcamera: Support for multi-context operations
Related show

Commit Message

Jacopo Mondi July 25, 2025, 10:33 a.m. UTC
Introduce the MediaContext class that represents a media device
context to which video devices and subdevices can be bound to.

A MediaContext can be created from a MediaDevice (which does not
support multi-context) and from SharedMediaDevice (which supports
multi-context).

Binding devices to a MediaContext created from MediaDevice is a nop,
while MediaContext created from SharedMediaDevice perform proper
bindings.

Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
---
 include/libcamera/internal/media_device.h |  23 ++++-
 src/libcamera/media_device.cpp            | 135 ++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+), 1 deletion(-)

Comments

Isaac Scott July 25, 2025, 10:50 a.m. UTC | #1
Hi Jacopo,

Thank you for the patch,

On Fri, 2025-07-25 at 12:33 +0200, Jacopo Mondi wrote:
> Introduce the MediaContext class that represents a media device
> context to which video devices and subdevices can be bound to.
> 
> A MediaContext can be created from a MediaDevice (which does not
> support multi-context) and from SharedMediaDevice (which supports
> multi-context).
> 
> Binding devices to a MediaContext created from MediaDevice is a nop,
> while MediaContext created from SharedMediaDevice perform proper
> bindings.
> 
> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
> ---
>  include/libcamera/internal/media_device.h |  23 ++++-
>  src/libcamera/media_device.cpp            | 135
> ++++++++++++++++++++++++++++++
>  2 files changed, 157 insertions(+), 1 deletion(-)
> 
> diff --git a/include/libcamera/internal/media_device.h
> b/include/libcamera/internal/media_device.h
> index
> ea0e9d66009933bbb9613d27423d2f951520c887..2dbfcc447d84bb6a1eee4ed598d
> 22b5cc45adbe9 100644
> --- a/include/libcamera/internal/media_device.h
> +++ b/include/libcamera/internal/media_device.h
> @@ -21,6 +21,9 @@
>  
>  namespace libcamera {
>  
> +class V4L2VideoDevice;
> +class V4L2Subdevice;
> +
>  class MediaDeviceFactory
>  {
>  public:
> @@ -28,6 +31,19 @@ public:
>  	createMediaDevice(const std::string &deviceNode);
>  };
>  
> +class MediaContext
> +{
> +public:
> +	MediaContext();
> +	MediaContext(UniqueFD &&fd);
> +
> +	int bindDevice(V4L2VideoDevice *dev);
> +	int bindDevice(V4L2Subdevice *dev);
> +
> +private:
> +	UniqueFD fd_;
> +};
> +
>  class MediaDevice : protected Loggable
>  {
>  public:
> @@ -63,10 +79,14 @@ public:
>  
>  	std::vector<MediaEntity *> locateEntities(unsigned int
> function);
>  
> +	virtual std::unique_ptr<MediaContext> createContext();
> +
>  protected:
>  	MediaDevice(const std::string &deviceNode);
>  	std::string logPrefix() const override;
>  
> +	std::string deviceNode_;
> +
>  private:
>  	friend class MediaDeviceFactory;
>  
> @@ -88,7 +108,6 @@ private:
>  	int setupLink(const MediaLink *link, unsigned int flags);
>  
>  	std::string driver_;
> -	std::string deviceNode_;
>  	std::string model_;
>  	unsigned int version_;
>  	unsigned int hwRevision_;
> @@ -111,6 +130,8 @@ public:
>  	bool lock() override;
>  	void unlock() override;
>  
> +	std::unique_ptr<MediaContext> createContext() override;
> +
>  private:
>  	friend class MediaDeviceFactory;
>  
> diff --git a/src/libcamera/media_device.cpp
> b/src/libcamera/media_device.cpp
> index
> 70d073f964bde0236585de083baca4cd9c30d193..2b7df346ad9916cf22641ea770e
> 0a1507921d89d 100644
> --- a/src/libcamera/media_device.cpp
> +++ b/src/libcamera/media_device.cpp
> @@ -21,6 +21,9 @@
>  
>  #include <libcamera/base/log.h>
>  
> +#include "libcamera/internal/v4l2_subdevice.h"
> +#include "libcamera/internal/v4l2_videodevice.h"
> +
>  /**
>   * \file media_device.h
>   * \brief Provide a representation of a Linux kernel Media
> Controller device
> @@ -81,6 +84,99 @@ MediaDeviceFactory::createMediaDevice(const
> std::string &deviceNode)
>  	return std::unique_ptr<MediaDevice>(new
> MediaDevice(deviceNode));
>  }
>  
> +/**
> + * \class MediaContext
> + * \brief Represents a media device context
> + *
> + * A media device context is obtained from a MediaDevice or a
> SharedMediaDevice
> + * instance with the MediaDevice::createContext() and
> + * SharedMediaDevice::createContext() functions.
> + *
> + * MediaContext instances obtained from a MediaDevice that does not
> support
> + * multi-context operations will not have a valid file descriptor
> associated as
> + * the media device they are created from doesn't support multiple
> open. For
> + * this reason binding a device to a context that is obtained from a
> MediaDevice
> + * is a nop.
> + *
> + * MediaContext instances obtained from a SharedMediaDevice that
> supports
> + * multi-context operations owns the file descriptor that identifies
> the media
> + * context, obtained by opening the media device multiple times and
> provided to
> + * the context at creation time. Binding devices to a context
> obtained from a
> + * SharedMediaDevice associates the devices to an execution context
> which
> + * remains valid until the last bound device stays valid.
> + *
> + * Users of this class can bind devices on valid and invalid
> MultiContext
> + * instances alike. If the kernel driver supports multi-context
> operations
> + * then the device can be opened multiple times and multiple
> contexts can be
> + * created to which devices can be bound to. If the kernel driver
> doesn't
> + * support multi-context operations then it can be opened a single
> time only
> + * and a creating contexts and bindings devices to it is effectively
> a nop.
> + */
> +
> +/**
> + * \brief Constructs a MediaContext not associated to any media
> device context
> + *
> + * Constructs a media device context not associated to any valid
> media device
> + * context file descriptor. Binding devices to such media device
> context
> + * is effectively a nop.
> + */
> +MediaContext::MediaContext()
> +{
> +}
> +
> +/**
> + * \brief Construct a MediaContext associated to a valid media
> device context
> + * \param[in] fd The file descriptor that identifies a media device
> context
> + *
> + * Construct a media device context to which devices can be bound
> to.
> + *
> + * Constructing a MediaContext with an associated valid media device
> context
> + * allows to create isolated execution contexts by binding devices
> to it.
> + *
> + * The file descriptor \a fd obtained by opening the media device
> and that
> + * identifies the media device context is moved in the newly
> constructed
> + * MediaContext which maintains its ownership and automatically
> closes it at
> + * destruction time.
> + */
> +MediaContext::MediaContext(UniqueFD &&fd)
> +	: fd_(std::move(fd))
> +{
> +}
> +
> +/**
> + * \brief Bind a V4L2 video device to a media device context
> + * \param[in] dev The video device to bind
> + *
> + * If the MediaContext has been created by a MediaDevice that does
> not
> + * support multi-context operations, this function is effectively a
> nop.
> + *
> + * \return 0 on success or a negative error code otherwise
> + */
> +int MediaContext::bindDevice(V4L2VideoDevice *dev)
> +{
> +	if (fd_.get() == -1)
> +		return 0;
> +
> +	return dev->bindContext(fd_.get());
> +}
> +
> +/**
> + * \brief Bind a V4L2 subdevice to a media device context
> + * \param[in] dev The subdevice to bind
> + *
> + * If the MediaContext has been created by a MediaDevice that does
> not
> + * support multi-context operations, this function is effectively a
> nop.
> + *
> + * \return 0 on success or a negative error code otherwise
> + */
> +int MediaContext::bindDevice(V4L2Subdevice *dev)
> +{
> +	if (fd_.get() == -1)
> +		return 0;
> +
> +	return dev->bindContext(fd_.get());
> +}
> +
>  /**
>   * \class MediaDevice
>   * \brief The MediaDevice represents a Media Controller device with
> its full
> @@ -603,6 +699,45 @@ int MediaDevice::disableLinks()
>   * driver unloading for most devices. The media device is passed as
> a parameter.
>   */
>  
> +/**
> + * \brief Create a MediaContext not associated with a media device
> context
> + * \return A MediaContext to which bindings devices is a nop
> + */
> +std::unique_ptr<MediaContext> MediaDevice::createContext()
> +{
> +	return std::make_unique<MediaContext>();
> +}
> +
> +/**
> + * \brief Create a MediaContext associated with a media device
> context
> + *
> + * Create a MediaContext by opening the media device and create a
> media
> + * device context using the obtained file descriptor.
> + *
> + * The open file descriptor is moved to the MediaContext whose
> ownership is
> + * moved to the caller of this function.
> + *
> + * \return A MediaContext to which is possible to bind devices to
> + */

s/to which is possible to bind devices to/which devices can be bound
to/

Otherwise,

Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>

> +std::unique_ptr<MediaContext> SharedMediaDevice::createContext()
> +{
> +	auto fd = UniqueFD(::open(deviceNode_.c_str(), O_RDWR |
> O_CLOEXEC));
> +	if (!fd.isValid()) {
> +		int ret = -errno;
> +		LOG(MediaDevice, Error)
> +			<< "Failed to open media device at "
> +			<< deviceNode_ << ": " << strerror(-ret);
> +		return {};
> +	}
> +
> +	return std::make_unique<MediaContext>(std::move(fd));
> +}
> +
> +/**
> + * \var MediaDevice::deviceNode_
> + * \brief The media device device node path
> + */
> +
>  /**
>   * \brief Open the media device
>   *

Patch
diff mbox series

diff --git a/include/libcamera/internal/media_device.h b/include/libcamera/internal/media_device.h
index ea0e9d66009933bbb9613d27423d2f951520c887..2dbfcc447d84bb6a1eee4ed598d22b5cc45adbe9 100644
--- a/include/libcamera/internal/media_device.h
+++ b/include/libcamera/internal/media_device.h
@@ -21,6 +21,9 @@ 
 
 namespace libcamera {
 
+class V4L2VideoDevice;
+class V4L2Subdevice;
+
 class MediaDeviceFactory
 {
 public:
@@ -28,6 +31,19 @@  public:
 	createMediaDevice(const std::string &deviceNode);
 };
 
+class MediaContext
+{
+public:
+	MediaContext();
+	MediaContext(UniqueFD &&fd);
+
+	int bindDevice(V4L2VideoDevice *dev);
+	int bindDevice(V4L2Subdevice *dev);
+
+private:
+	UniqueFD fd_;
+};
+
 class MediaDevice : protected Loggable
 {
 public:
@@ -63,10 +79,14 @@  public:
 
 	std::vector<MediaEntity *> locateEntities(unsigned int function);
 
+	virtual std::unique_ptr<MediaContext> createContext();
+
 protected:
 	MediaDevice(const std::string &deviceNode);
 	std::string logPrefix() const override;
 
+	std::string deviceNode_;
+
 private:
 	friend class MediaDeviceFactory;
 
@@ -88,7 +108,6 @@  private:
 	int setupLink(const MediaLink *link, unsigned int flags);
 
 	std::string driver_;
-	std::string deviceNode_;
 	std::string model_;
 	unsigned int version_;
 	unsigned int hwRevision_;
@@ -111,6 +130,8 @@  public:
 	bool lock() override;
 	void unlock() override;
 
+	std::unique_ptr<MediaContext> createContext() override;
+
 private:
 	friend class MediaDeviceFactory;
 
diff --git a/src/libcamera/media_device.cpp b/src/libcamera/media_device.cpp
index 70d073f964bde0236585de083baca4cd9c30d193..2b7df346ad9916cf22641ea770e0a1507921d89d 100644
--- a/src/libcamera/media_device.cpp
+++ b/src/libcamera/media_device.cpp
@@ -21,6 +21,9 @@ 
 
 #include <libcamera/base/log.h>
 
+#include "libcamera/internal/v4l2_subdevice.h"
+#include "libcamera/internal/v4l2_videodevice.h"
+
 /**
  * \file media_device.h
  * \brief Provide a representation of a Linux kernel Media Controller device
@@ -81,6 +84,99 @@  MediaDeviceFactory::createMediaDevice(const std::string &deviceNode)
 	return std::unique_ptr<MediaDevice>(new MediaDevice(deviceNode));
 }
 
+/**
+ * \class MediaContext
+ * \brief Represents a media device context
+ *
+ * A media device context is obtained from a MediaDevice or a SharedMediaDevice
+ * instance with the MediaDevice::createContext() and
+ * SharedMediaDevice::createContext() functions.
+ *
+ * MediaContext instances obtained from a MediaDevice that does not support
+ * multi-context operations will not have a valid file descriptor associated as
+ * the media device they are created from doesn't support multiple open. For
+ * this reason binding a device to a context that is obtained from a MediaDevice
+ * is a nop.
+ *
+ * MediaContext instances obtained from a SharedMediaDevice that supports
+ * multi-context operations owns the file descriptor that identifies the media
+ * context, obtained by opening the media device multiple times and provided to
+ * the context at creation time. Binding devices to a context obtained from a
+ * SharedMediaDevice associates the devices to an execution context which
+ * remains valid until the last bound device stays valid.
+ *
+ * Users of this class can bind devices on valid and invalid MultiContext
+ * instances alike. If the kernel driver supports multi-context operations
+ * then the device can be opened multiple times and multiple contexts can be
+ * created to which devices can be bound to. If the kernel driver doesn't
+ * support multi-context operations then it can be opened a single time only
+ * and a creating contexts and bindings devices to it is effectively a nop.
+ */
+
+/**
+ * \brief Constructs a MediaContext not associated to any media device context
+ *
+ * Constructs a media device context not associated to any valid media device
+ * context file descriptor. Binding devices to such media device context
+ * is effectively a nop.
+ */
+MediaContext::MediaContext()
+{
+}
+
+/**
+ * \brief Construct a MediaContext associated to a valid media device context
+ * \param[in] fd The file descriptor that identifies a media device context
+ *
+ * Construct a media device context to which devices can be bound to.
+ *
+ * Constructing a MediaContext with an associated valid media device context
+ * allows to create isolated execution contexts by binding devices to it.
+ *
+ * The file descriptor \a fd obtained by opening the media device and that
+ * identifies the media device context is moved in the newly constructed
+ * MediaContext which maintains its ownership and automatically closes it at
+ * destruction time.
+ */
+MediaContext::MediaContext(UniqueFD &&fd)
+	: fd_(std::move(fd))
+{
+}
+
+/**
+ * \brief Bind a V4L2 video device to a media device context
+ * \param[in] dev The video device to bind
+ *
+ * If the MediaContext has been created by a MediaDevice that does not
+ * support multi-context operations, this function is effectively a nop.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int MediaContext::bindDevice(V4L2VideoDevice *dev)
+{
+	if (fd_.get() == -1)
+		return 0;
+
+	return dev->bindContext(fd_.get());
+}
+
+/**
+ * \brief Bind a V4L2 subdevice to a media device context
+ * \param[in] dev The subdevice to bind
+ *
+ * If the MediaContext has been created by a MediaDevice that does not
+ * support multi-context operations, this function is effectively a nop.
+ *
+ * \return 0 on success or a negative error code otherwise
+ */
+int MediaContext::bindDevice(V4L2Subdevice *dev)
+{
+	if (fd_.get() == -1)
+		return 0;
+
+	return dev->bindContext(fd_.get());
+}
+
 /**
  * \class MediaDevice
  * \brief The MediaDevice represents a Media Controller device with its full
@@ -603,6 +699,45 @@  int MediaDevice::disableLinks()
  * driver unloading for most devices. The media device is passed as a parameter.
  */
 
+/**
+ * \brief Create a MediaContext not associated with a media device context
+ * \return A MediaContext to which bindings devices is a nop
+ */
+std::unique_ptr<MediaContext> MediaDevice::createContext()
+{
+	return std::make_unique<MediaContext>();
+}
+
+/**
+ * \brief Create a MediaContext associated with a media device context
+ *
+ * Create a MediaContext by opening the media device and create a media
+ * device context using the obtained file descriptor.
+ *
+ * The open file descriptor is moved to the MediaContext whose ownership is
+ * moved to the caller of this function.
+ *
+ * \return A MediaContext to which is possible to bind devices to
+ */
+std::unique_ptr<MediaContext> SharedMediaDevice::createContext()
+{
+	auto fd = UniqueFD(::open(deviceNode_.c_str(), O_RDWR | O_CLOEXEC));
+	if (!fd.isValid()) {
+		int ret = -errno;
+		LOG(MediaDevice, Error)
+			<< "Failed to open media device at "
+			<< deviceNode_ << ": " << strerror(-ret);
+		return {};
+	}
+
+	return std::make_unique<MediaContext>(std::move(fd));
+}
+
+/**
+ * \var MediaDevice::deviceNode_
+ * \brief The media device device node path
+ */
+
 /**
  * \brief Open the media device
  *