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