[RFC,v1,12/20] libcamera: camera_manager: Add CameraManager::enumerate()
diff mbox series

Message ID 20260918080734.1228227-13-naush@raspberrypi.com
State New
Headers show
Series
  • libcamera: New enumeration API
Related show

Commit Message

Naushir Patuck Sept. 18, 2026, 7:59 a.m. UTC
Add a CameraManager::enumerate() function that returns a descriptor for
every camera that the pipeline handlers can report without actually
creating it. The descriptors are cached as media devices in use are
skipped by later surveys.

At present, Cameras cannot be created from a descriptor yet, so
enumerate() is informational only.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
---
 include/libcamera/camera_manager.h          |  3 +
 include/libcamera/internal/camera_manager.h |  9 ++
 src/libcamera/camera_manager.cpp            | 98 +++++++++++++++++++++
 3 files changed, 110 insertions(+)

Patch
diff mbox series

diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h
index 27835500f781..5e0b95ef1aab 100644
--- a/include/libcamera/camera_manager.h
+++ b/include/libcamera/camera_manager.h
@@ -20,6 +20,7 @@ 
 namespace libcamera {
 
 class Camera;
+class CameraDescriptor;
 
 class CameraManager : public Object, public Extensible
 {
@@ -31,6 +32,8 @@  public:
 	int start();
 	void stop();
 
+	std::vector<std::shared_ptr<CameraDescriptor>> enumerate();
+
 	std::vector<std::shared_ptr<Camera>> cameras() const;
 	std::shared_ptr<Camera> get(std::string_view id);
 
diff --git a/include/libcamera/internal/camera_manager.h b/include/libcamera/internal/camera_manager.h
index ed90d1d471a0..10e7797c4a29 100644
--- a/include/libcamera/internal/camera_manager.h
+++ b/include/libcamera/internal/camera_manager.h
@@ -25,6 +25,7 @@ 
 namespace libcamera {
 
 class Camera;
+class CameraDescriptor;
 class DeviceEnumerator;
 class IPAManager;
 class MediaDevice;
@@ -39,6 +40,7 @@  public:
 	Private();
 
 	int start();
+	std::vector<std::shared_ptr<CameraDescriptor>> enumerate();
 	void addCamera(std::shared_ptr<Camera> camera) LIBCAMERA_TSA_EXCLUDES(mutex_);
 	void removeCamera(std::shared_ptr<Camera> camera) LIBCAMERA_TSA_EXCLUDES(mutex_);
 
@@ -59,6 +61,11 @@  private:
 	void createPipelineHandlers();
 	void pipelineFactoryMatch(const PipelineHandlerFactoryBase *factory);
 	std::shared_ptr<PipelineHandler> findMatchingHandler(const MediaDevice *media);
+
+	std::vector<std::shared_ptr<CameraDescriptor>> surveyThread() LIBCAMERA_TSA_EXCLUDES(mutex_);
+	void surveyFactory(const PipelineHandlerFactoryBase *factory) LIBCAMERA_TSA_EXCLUDES(mutex_);
+	std::shared_ptr<Camera> initializeThread(std::shared_ptr<CameraDescriptor> descriptor);
+
 	void cleanup() LIBCAMERA_TSA_EXCLUDES(mutex_);
 
 	/*
@@ -75,6 +82,8 @@  private:
 	int status_ LIBCAMERA_TSA_GUARDED_BY(mutex_);
 
 	bool started_ LIBCAMERA_TSA_GUARDED_BY(mutex_) = false;
+	/* Known camera descriptors, accessed from the CameraManager thread only. */
+	std::vector<std::shared_ptr<CameraDescriptor>> descriptors_;
 
 	std::unique_ptr<DeviceEnumerator> enumerator_;
 
diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index 55532179a61c..5cd0f50de12a 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -7,13 +7,17 @@ 
 
 #include "libcamera/internal/camera_manager.h"
 
+#include <algorithm>
+
 #include <libcamera/base/log.h>
 #include <libcamera/base/utils.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/camera_descriptor.h>
 #include <libcamera/property_ids.h>
 
 #include "libcamera/internal/camera.h"
+#include "libcamera/internal/camera_descriptor.h"
 #include "libcamera/internal/device_enumerator.h"
 #include "libcamera/internal/global_configuration.h"
 #include "libcamera/internal/ipa_manager.h"
@@ -99,6 +103,19 @@  int CameraManager::Private::startThread()
 	return 0;
 }
 
+/*
+ * Enumerate the cameras in the system without initialising them, starting the
+ * camera manager thread on first use. Called from the application thread.
+ */
+std::vector<std::shared_ptr<CameraDescriptor>> CameraManager::Private::enumerate()
+{
+	int ret = startThread();
+	if (ret)
+		return {};
+
+	return invokeMethod(&Private::surveyThread, ConnectionTypeBlocking);
+}
+
 void CameraManager::Private::run()
 {
 	LOG(Camera, Debug) << "Starting camera manager";
@@ -255,6 +272,62 @@  CameraManager::Private::findMatchingHandler(const MediaDevice *media)
 	return match;
 }
 
+/*
+ * Survey all pipeline handler factories and return the known camera
+ * descriptors. Descriptors are cached because later surveys skip the media
+ * devices in use by live pipeline handler instances, and would otherwise drop
+ * the cameras already initialised. Called on the CM thread.
+ */
+std::vector<std::shared_ptr<CameraDescriptor>> CameraManager::Private::surveyThread()
+{
+	ASSERT(Thread::current() == this);
+
+	for (const PipelineHandlerFactoryBase *factory : pipelineFactories())
+		surveyFactory(factory);
+
+	return descriptors_;
+}
+
+/*
+ * Survey the cameras of a single pipeline handler factory and cache their
+ * descriptors. Called on the CM thread.
+ */
+void CameraManager::Private::surveyFactory(const PipelineHandlerFactoryBase *factory)
+{
+	ASSERT(Thread::current() == this);
+
+	CameraManager *const o = LIBCAMERA_O_PTR();
+
+	std::shared_ptr<PipelineHandler> pipe = factory->create(o);
+	std::vector<std::shared_ptr<CameraDescriptor>> descriptors;
+	int ret = pipe->survey(enumerator_.get(), &descriptors);
+	if (ret == -ENOTSUP) {
+		/* The pipeline handler does not support surveying. */
+		return;
+	} else if (ret < 0) {
+		LOG(Camera, Error)
+			<< "Failed to survey cameras for pipeline handler "
+			<< factory->name() << ": " << strerror(-ret);
+		return;
+	}
+
+	for (std::shared_ptr<CameraDescriptor> &descriptor : descriptors) {
+		/*
+		 * Record the factory that produced the descriptor, so that
+		 * initialize() can create a pipeline handler for it.
+		 */
+		descriptor->_d()->factory_ = factory;
+
+		auto match = [&](const auto &d) {
+			return d->id() == descriptor->id();
+		};
+		if (std::any_of(descriptors_.begin(), descriptors_.end(), match))
+			continue;
+
+		descriptors_.push_back(std::move(descriptor));
+	}
+}
+
 void CameraManager::Private::cleanup()
 {
 	enumerator_->devicesAdded.disconnect(this);
@@ -264,6 +337,8 @@  void CameraManager::Private::cleanup()
 		started_ = false;
 	}
 
+	descriptors_.clear();
+
 	/*
 	 * Release all references to cameras to ensure they all get destroyed
 	 * before the device enumerator deletes the media devices. Cameras are
@@ -457,6 +532,29 @@  void CameraManager::stop()
 	d->wait();
 }
 
+/**
+ * \brief Enumerate the cameras in the system without initialising them
+ *
+ * Enumerate the devices in the system and return a descriptor for every
+ * camera found, without initialising any camera.
+ *
+ * Only cameras of pipeline handlers that support surveying are reported.
+ * Cameras of other pipeline handlers are created by start() and reported by
+ * cameras() as before.
+ *
+ * Descriptors are reported in match order, grouped by pipeline handler.
+ * Descriptors of cameras removed from the system remain listed until the
+ * camera manager is stopped.
+ *
+ * This function starts the camera manager if it is not yet running.
+ *
+ * \return A list of descriptors for the cameras found in the system
+ */
+std::vector<std::shared_ptr<CameraDescriptor>> CameraManager::enumerate()
+{
+	return _d()->enumerate();
+}
+
 /**
  * \fn CameraManager::cameras()
  * \brief Retrieve all available cameras