mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	spa: libcamera: fix CameraManager event handling
libcamera's CameraManager runs the event handlers on its own thread, thus synchronization is needed to ensure correct functionality. Implement that by collecting hotplug events into a mutex protected queue and signalling the main loop where they can be processed and the "object_info" event can be safely emitted.
This commit is contained in:
		
							parent
							
								
									f9796fc024
								
							
						
					
					
						commit
						0f6c5a04c7
					
				
					 1 changed files with 107 additions and 18 deletions
				
			
		| 
						 | 
					@ -30,6 +30,9 @@
 | 
				
			||||||
#include <sys/stat.h>
 | 
					#include <sys/stat.h>
 | 
				
			||||||
#include <fcntl.h>
 | 
					#include <fcntl.h>
 | 
				
			||||||
#include <utility>
 | 
					#include <utility>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <optional>
 | 
				
			||||||
 | 
					#include <queue>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <libcamera/camera.h>
 | 
					#include <libcamera/camera.h>
 | 
				
			||||||
#include <libcamera/camera_manager.h>
 | 
					#include <libcamera/camera_manager.h>
 | 
				
			||||||
| 
						 | 
					@ -66,6 +69,7 @@ struct impl {
 | 
				
			||||||
	struct spa_device device = {};
 | 
						struct spa_device device = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct spa_log *log;
 | 
						struct spa_log *log;
 | 
				
			||||||
 | 
						struct spa_loop_utils *loop_utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct spa_hook_list hooks;
 | 
						struct spa_hook_list hooks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +83,21 @@ struct impl {
 | 
				
			||||||
	struct device devices[MAX_DEVICES];
 | 
						struct device devices[MAX_DEVICES];
 | 
				
			||||||
	uint32_t n_devices = 0;
 | 
						uint32_t n_devices = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	impl(spa_log *log);
 | 
						struct hotplug_event {
 | 
				
			||||||
 | 
							enum class type { add, remove } type;
 | 
				
			||||||
 | 
							std::shared_ptr<Camera> camera;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::mutex hotplug_events_lock;
 | 
				
			||||||
 | 
						std::queue<hotplug_event> hotplug_events;
 | 
				
			||||||
 | 
						struct spa_source *hotplug_event_source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl(spa_log *log, spa_loop_utils *loop_utils, spa_source *hotplug_event_source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~impl()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							spa_loop_utils_destroy_source(loop_utils, hotplug_event_source);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -168,32 +186,88 @@ static int emit_object_info(struct impl *impl, struct device *device)
 | 
				
			||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void impl::addCamera(std::shared_ptr<Camera> camera)
 | 
					static void try_add_camera(struct impl *impl, std::shared_ptr<Camera> camera)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = this;
 | 
					 | 
				
			||||||
	struct device *device;
 | 
						struct device *device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_log_info(impl->log, "new camera: %s", camera->id().c_str());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((device = find_device(impl, camera.get())) != NULL)
 | 
						if ((device = find_device(impl, camera.get())) != NULL)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((device = add_device(impl, std::move(camera))) == NULL)
 | 
						if ((device = add_device(impl, std::move(camera))) == NULL)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_info(impl->log, "camera added: %s", device->camera->id().c_str());
 | 
				
			||||||
	emit_object_info(impl, device);
 | 
						emit_object_info(impl, device);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void try_remove_camera(struct impl *impl, const Camera *camera)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device *device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((device = find_device(impl, camera)) == NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_info(impl->log, "camera removed: %s", device->camera->id().c_str());
 | 
				
			||||||
 | 
						remove_device(impl, device);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void consume_hotplug_event(struct impl *impl, impl::hotplug_event& event)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto& [ type, camera ] = event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case impl::hotplug_event::type::add:
 | 
				
			||||||
 | 
							spa_log_info(impl->log, "camera appeared: %s", camera->id().c_str());
 | 
				
			||||||
 | 
							try_add_camera(impl, std::move(camera));
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case impl::hotplug_event::type::remove:
 | 
				
			||||||
 | 
							spa_log_info(impl->log, "camera disappeared: %s", camera->id().c_str());
 | 
				
			||||||
 | 
							try_remove_camera(impl, camera.get());
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void on_hotplug_event(void *data, std::uint64_t)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto impl = static_cast<struct impl *>(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (;;) {
 | 
				
			||||||
 | 
							std::optional<impl::hotplug_event> event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								std::unique_lock guard(impl->hotplug_events_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!impl->hotplug_events.empty()) {
 | 
				
			||||||
 | 
									event = std::move(impl->hotplug_events.front());
 | 
				
			||||||
 | 
									impl->hotplug_events.pop();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!event)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							consume_hotplug_event(impl, *event);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void impl::addCamera(std::shared_ptr<Camera> camera)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock guard(hotplug_events_lock);
 | 
				
			||||||
 | 
							hotplug_events.push({ hotplug_event::type::add, std::move(camera) });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_loop_utils_signal_event(loop_utils, hotplug_event_source);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void impl::removeCamera(std::shared_ptr<Camera> camera)
 | 
					void impl::removeCamera(std::shared_ptr<Camera> camera)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = this;
 | 
						{
 | 
				
			||||||
	struct device *device;
 | 
							std::unique_lock guard(hotplug_events_lock);
 | 
				
			||||||
 | 
							hotplug_events.push({ hotplug_event::type::remove, std::move(camera) });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_log_info(impl->log, "camera removed: %s", camera->id().c_str());
 | 
						spa_loop_utils_signal_event(loop_utils, hotplug_event_source);
 | 
				
			||||||
	if ((device = find_device(impl, camera.get())) == NULL)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	remove_device(impl, device);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int start_monitor(struct impl *impl)
 | 
					static int start_monitor(struct impl *impl)
 | 
				
			||||||
| 
						 | 
					@ -217,9 +291,9 @@ static int enum_devices(struct impl *impl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	auto cameras = impl->manager->cameras();
 | 
						auto cameras = impl->manager->cameras();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (const std::shared_ptr<Camera> &cam : cameras) {
 | 
						for (std::shared_ptr<Camera>& camera : cameras)
 | 
				
			||||||
		impl->addCamera(cam);
 | 
							try_add_camera(impl, std::move(camera));
 | 
				
			||||||
	}
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -316,9 +390,11 @@ static int impl_clear(struct spa_handle *handle)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl::impl(spa_log *log)
 | 
					impl::impl(spa_log *log, spa_loop_utils *loop_utils, spa_source *hotplug_event_source)
 | 
				
			||||||
	: handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }),
 | 
						: handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }),
 | 
				
			||||||
	  log(log)
 | 
						  log(log),
 | 
				
			||||||
 | 
						  loop_utils(loop_utils),
 | 
				
			||||||
 | 
						  hotplug_event_source(hotplug_event_source)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	libcamera_log_topic_init(log);
 | 
						libcamera_log_topic_init(log);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -349,7 +425,20 @@ impl_init(const struct spa_handle_factory *factory,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log));
 | 
						auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	new (handle) impl(log);
 | 
						auto loop_utils = static_cast<spa_loop_utils *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils));
 | 
				
			||||||
 | 
						if (!loop_utils) {
 | 
				
			||||||
 | 
							spa_log_error(log, "a " SPA_TYPE_INTERFACE_LoopUtils " is needed");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						auto hotplug_event_source = spa_loop_utils_add_event(loop_utils, on_hotplug_event, handle);
 | 
				
			||||||
 | 
						if (!hotplug_event_source) {
 | 
				
			||||||
 | 
							int res = -errno;
 | 
				
			||||||
 | 
							spa_log_error(log, "failed to create hotplug event: %m");
 | 
				
			||||||
 | 
							return res;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						new (handle) impl(log, loop_utils, hotplug_event_source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue