From 19d9bb72191521bf7bec0a503287980d98f3f1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Fri, 1 Aug 2025 12:44:19 +0200 Subject: [PATCH] spa: libcamera: source: create eventfd before starting camera An eventfd is used to signal the data loop from the libcamera request completion event handler. Previously, this eventfd was created and installed after the camera has been started and requests were queued. This is problematic because it creates a small time frame where the libcamera request completion handler will run in a state where the eventfd is not fully set up. Fix that by settup up the eventfd before the camera is started. (cherry picked from commit e0e8bf083d6599ca1a2b0f8e9a2a63e643316d1e) --- spa/plugins/libcamera/libcamera-source.cpp | 67 ++++++++++++---------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/spa/plugins/libcamera/libcamera-source.cpp b/spa/plugins/libcamera/libcamera-source.cpp index e771873aa..6b26c94e8 100644 --- a/spa/plugins/libcamera/libcamera-source.cpp +++ b/spa/plugins/libcamera/libcamera-source.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -1209,6 +1210,19 @@ void impl::requestComplete(libcamera::Request *request) } +int do_remove_source(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + auto *impl = static_cast(user_data); + if (impl->source.loop) + spa_loop_remove_source(loop, &impl->source); + return 0; +} + int spa_libcamera_stream_on(struct impl *impl) { struct port *port = &impl->out_ports[0]; @@ -1222,54 +1236,45 @@ int spa_libcamera_stream_on(struct impl *impl) if (impl->active) return 0; - impl->camera->requestCompleted.connect(impl, &impl::requestComplete); + res = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + if (res < 0) + return res; + + impl->source.fd = res; + impl->source.func = libcamera_on_fd_events; + impl->source.data = impl; + impl->source.mask = SPA_IO_IN | SPA_IO_ERR; + impl->source.rmask = 0; + res = spa_loop_add_source(impl->data_loop, &impl->source); + if (res < 0) + goto err_close_source; spa_log_info(impl->log, "starting camera %s", impl->camera->id().c_str()); if ((res = impl->camera->start(&impl->initial_controls)) < 0) - goto error; + goto err_remove_source; + + impl->camera->requestCompleted.connect(impl, &impl::requestComplete); for (Request *req : impl->pendingRequests) { if ((res = impl->camera->queueRequest(req)) < 0) - goto error_stop; + goto err_stop_camera; } impl->pendingRequests.clear(); impl->dll.bw = 0.0; - - impl->source.func = libcamera_on_fd_events; - impl->source.data = impl; - impl->source.fd = spa_system_eventfd_create(impl->system, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - impl->source.mask = SPA_IO_IN | SPA_IO_ERR; - impl->source.rmask = 0; - if (impl->source.fd < 0) { - spa_log_error(impl->log, "Failed to create eventfd: %s", spa_strerror(impl->source.fd)); - res = impl->source.fd; - goto error_stop; - } - spa_loop_add_source(impl->data_loop, &impl->source); - impl->active = true; return 0; -error_stop: +err_stop_camera: impl->camera->stop(); -error: impl->camera->requestCompleted.disconnect(impl, &impl::requestComplete); - return res == -EACCES ? -EBUSY : res; -} +err_remove_source: + spa_loop_invoke(impl->data_loop, do_remove_source, 0, nullptr, 0, true, impl); +err_close_source: + spa_system_close(impl->system, std::exchange(impl->source.fd, -1)); -int do_remove_source(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - auto *impl = static_cast(user_data); - if (impl->source.loop) - spa_loop_remove_source(loop, &impl->source); - return 0; + return res == -EACCES ? -EBUSY : res; } int spa_libcamera_stream_off(struct impl *impl)