diff --git a/spa/plugins/libcamera/libcamera-source.cpp b/spa/plugins/libcamera/libcamera-source.cpp index 08f92c5d7..fd21aff4d 100644 --- a/spa/plugins/libcamera/libcamera-source.cpp +++ b/spa/plugins/libcamera/libcamera-source.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -121,6 +123,7 @@ struct impl { struct spa_node node = {}; struct spa_log *log; + struct spa_loop *main_loop; struct spa_loop *data_loop; struct spa_system *system; @@ -161,10 +164,12 @@ struct impl { ControlList ctrls; ControlList initial_controls; + ControlList request_metadata; + std::mutex request_metadata_mutex; bool active = false; bool acquired = false; - impl(spa_log *log, spa_loop *data_loop, spa_system *system, + impl(spa_log *log, spa_loop *main_loop, spa_loop *data_loop, spa_system *system, std::shared_ptr manager, std::shared_ptr camera, std::unique_ptr config); @@ -1037,6 +1042,23 @@ int spa_libcamera_apply_controls(struct impl *impl, libcamera::ControlList&& con ); } +void emit_node_info(struct impl *impl, bool full); + +static int do_emit_node_info(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *impl = (struct impl *)user_data; + + impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + impl->params[NODE_Props].flags ^= SPA_PARAM_INFO_SERIAL; + emit_node_info(impl, true); + return 0; +} + void handle_completed_request(struct impl *impl, libcamera::Request *request) { const auto request_id = request->cookie(); @@ -1111,6 +1133,34 @@ void handle_completed_request(struct impl *impl, libcamera::Request *request) d->chunk->flags |= SPA_CHUNK_FLAG_CORRUPTED; } + // update stored metadata from request metadata + { + const auto lock_guard = std::lock_guard{impl->request_metadata_mutex}; + + auto update = false; + for (const auto& [id, value] : request->metadata()) + { + const auto prop_id = control_to_prop_id(id); + if (prop_id >= SPA_PROP_START_CUSTOM) + { + continue; + } + const auto it = std::find_if(impl->request_metadata.begin(), impl->request_metadata.end(), [id](const auto& entry) + { + return id == entry.first; + }); + if ((it == impl->request_metadata.end()) || (value != it->second)) + { + impl->request_metadata.set(id, value); + update = true; + } + } + if (update) + { + spa_loop_invoke(impl->main_loop, do_emit_node_info, 0, nullptr, 0, false, impl); + } + } + request->reuse(libcamera::Request::ReuseFlag::ReuseBuffers); spa_list_append(&port->queue, &b->link); @@ -1305,7 +1355,6 @@ spa_libcamera_alloc_buffers(struct impl *impl, struct port *port, return 0; } - void impl::requestComplete(libcamera::Request *request) { struct impl *impl = this; @@ -1451,7 +1500,8 @@ int impl_node_enum_params(void *object, int seq, { struct impl *impl = (struct impl*)object; struct spa_pod *param; - struct spa_pod_builder b = { 0 }; + spa_auto(spa_pod_dynamic_builder) b = { 0 }; + struct spa_pod_builder_state state; uint8_t buffer[1024]; struct spa_result_node_params result; uint32_t count = 0; @@ -1460,12 +1510,15 @@ int impl_node_enum_params(void *object, int seq, spa_return_val_if_fail(impl != nullptr, -EINVAL); spa_return_val_if_fail(num != 0, -EINVAL); + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + spa_pod_builder_get_state(&b.b, &state); + result.id = id; result.next = start; next: result.index = result.next++; - spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_pod_builder_reset(&b.b, &state); switch (id) { case SPA_PARAM_PropInfo: @@ -1481,6 +1534,39 @@ next: case SPA_PARAM_Props: { switch (result.index) { + case 0: { + const auto add_property = [&b, &impl](unsigned int controlId, const libcamera::ControlValue& value) { + const auto id = control_to_prop_id(controlId); + if (id >= SPA_PROP_START_CUSTOM) { + return; + } + if (value.isArray()) { + return; // not yet supported + } + spa_pod_builder_prop(&b.b, id, 0); + control_value_to_pod(b.b, value); + }; + + struct spa_pod_frame f; + spa_pod_builder_push_object(&b.b, &f, SPA_TYPE_OBJECT_Props, id); + const auto metadata = [&]() + { + const auto lock_guard = std::lock_guard{impl->request_metadata_mutex}; + auto metadata = impl->request_metadata; + return metadata; + }(); + for (const auto& [id, value] : metadata) + { + add_property(id, value); + } + for (const auto& [id, info] : impl->camera->controls()) { + if (!metadata.contains(id->id())) { + add_property(id->id(), info.def()); + } + } + param = (spa_pod*)spa_pod_builder_pop(&b.b, &f); + } + break; default: return 0; } @@ -1490,14 +1576,14 @@ next: return spa_libcamera_enum_format(impl, GET_OUT_PORT(impl, 0), seq, start, num, filter); case SPA_PARAM_Format: - if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, ¶m, &b)) <= 0) + if ((res = port_get_format(impl, GET_OUT_PORT(impl, 0), result.index, filter, ¶m, &b.b)) <= 0) return res; break; default: return -ENOENT; } - if (spa_pod_filter(&b, &result.param, param, filter) < 0) + if (spa_pod_filter(&b.b, &result.param, param, filter) < 0) goto next; spa_node_emit_result(&impl->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); @@ -2144,11 +2230,12 @@ int impl_clear(struct spa_handle *handle) return 0; } -impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system, +impl::impl(spa_log *log, spa_loop *main_loop, spa_loop *data_loop, spa_system *system, std::shared_ptr manager, std::shared_ptr camera, std::unique_ptr config) : handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }), log(log), + main_loop(main_loop), data_loop(data_loop), system(system), out_ports{{this}}, @@ -2200,9 +2287,15 @@ impl_init(const struct spa_handle_factory *factory, spa_return_val_if_fail(handle != nullptr, -EINVAL); auto log = static_cast(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log)); + auto main_loop = static_cast(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop)); auto data_loop = static_cast(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop)); auto system = static_cast(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System)); + if (!main_loop) { + spa_log_error(log, "a main_loop is needed"); + return -EINVAL; + } + if (!data_loop) { spa_log_error(log, "a data_loop is needed"); return -EINVAL; @@ -2235,7 +2328,7 @@ impl_init(const struct spa_handle_factory *factory, return -EINVAL; } - new (handle) impl(log, data_loop, system, + new (handle) impl(log, main_loop, data_loop, system, std::move(manager), std::move(camera), std::move(config));